From 2e90c4e1639acb1b940455f335f8467eed89f8d9 Mon Sep 17 00:00:00 2001 From: mccallum Date: Fri, 22 Mar 1996 00:37:53 +0000 Subject: [PATCH] New file. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1241 72102866-910b-0410-8b05-ffd578937521 --- Headers/gnustep/base/numbers.h | 85 ++ Headers/gnustep/base/o_x_bas.h.in | 126 +++ Headers/gnustep/base/o_x_cbs.h.in | 69 ++ Source/array.m | 910 +++++++++++++++++++ Source/data.m | 1026 +++++++++++++++++++++ Source/hash.m | 1130 +++++++++++++++++++++++ Source/list.m | 906 +++++++++++++++++++ Source/map.m | 1379 +++++++++++++++++++++++++++++ Source/numbers.c | 64 ++ Source/o_array.m | 910 +++++++++++++++++++ Source/o_cbs.m | 151 ++++ Source/o_cbs_char_p.m | 133 +++ Source/o_cbs_id.m | 95 ++ Source/o_cbs_int.m | 92 ++ Source/o_cbs_int_p.m | 94 ++ Source/o_cbs_void_p.m | 151 ++++ Source/o_hash.m | 1130 +++++++++++++++++++++++ Source/o_list.m | 906 +++++++++++++++++++ Source/o_map.m | 1379 +++++++++++++++++++++++++++++ Source/o_x_bas.m.in | 261 ++++++ Source/o_x_cbs.m.in | 87 ++ Source/objects/numbers.h | 85 ++ Source/objects/x-bas.h.in | 126 +++ Source/objects/x-cbs.h.in | 69 ++ 24 files changed, 11364 insertions(+) create mode 100644 Headers/gnustep/base/numbers.h create mode 100644 Headers/gnustep/base/o_x_bas.h.in create mode 100644 Headers/gnustep/base/o_x_cbs.h.in create mode 100644 Source/array.m create mode 100644 Source/data.m create mode 100644 Source/hash.m create mode 100644 Source/list.m create mode 100644 Source/map.m create mode 100644 Source/numbers.c create mode 100644 Source/o_array.m create mode 100644 Source/o_cbs.m create mode 100644 Source/o_cbs_char_p.m create mode 100644 Source/o_cbs_id.m create mode 100644 Source/o_cbs_int.m create mode 100644 Source/o_cbs_int_p.m create mode 100644 Source/o_cbs_void_p.m create mode 100644 Source/o_hash.m create mode 100644 Source/o_list.m create mode 100644 Source/o_map.m create mode 100644 Source/o_x_bas.m.in create mode 100644 Source/o_x_cbs.m.in create mode 100644 Source/objects/numbers.h create mode 100644 Source/objects/x-bas.h.in create mode 100644 Source/objects/x-cbs.h.in diff --git a/Headers/gnustep/base/numbers.h b/Headers/gnustep/base/numbers.h new file mode 100644 index 000000000..5ad2dda8b --- /dev/null +++ b/Headers/gnustep/base/numbers.h @@ -0,0 +1,85 @@ +/* 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:28:01 EST 1995 + * Updated: Mon Mar 18 14:36:49 EST 1996 + * Serial: 96.03.18.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. */ + +#ifndef __numbers_h_OBJECTS_INCLUDE +#define __numbers_h_OBJECTS_INCLUDE 1 + +/**** Included Headers *******************************************************/ + +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/** Magic numbers... **/ + +/* Magic numbers for the different types of structures... */ +#define OBJECTS_MAGIC_ARRAY 0x1b658008 /* Thu Mar 2 02:28:50 EST 1994 */ +#define OBJECTS_MAGIC_DATA 0x1b651971 /* Fri Nov 24 21:46:14 EST 1995 */ +#define OBJECTS_MAGIC_HASH 0x1b653ee5 /* ??? ??? ?? ??:??:?? ??? 1993 */ +#define OBJECTS_MAGIC_HEAP 0x1b65beef /* Tue Sep 5 17:21:34 EDT 1995 */ +#define OBJECTS_MAGIC_LIST 0x1b65600d /* Tue Sep 5 17:23:50 EDT 1995 */ +#define OBJECTS_MAGIC_MAP 0x1b65abba /* ??? ??? ?? ??:??:?? ??? 1993 */ + +/* WARNING: Don't use these. They are not guaranteed to remain in future + * editions of this file. They are here only as a cheap fix for an + * annoying little problem. */ +/* FIXME: Get rid of these. See `x-basics.[ch].in' + * and `x-callbacks.[ch].in'. */ +#define _OBJECTS_MAGIC_array OBJECTS_MAGIC_ARRAY +#define _OBJECTS_MAGIC_data OBJECTS_MAGIC_DATA +#define _OBJECTS_MAGIC_hash OBJECTS_MAGIC_HASH +#define _OBJECTS_MAGIC_heap OBJECTS_MAGIC_HEAP +#define _OBJECTS_MAGIC_list OBJECTS_MAGIC_LIST +#define _OBJECTS_MAGIC_map OBJECTS_MAGIC_MAP + +/* Internal counters for the three functions below. They are placed here + * purely for your viewing pleasure. WARNING: Do not mess with these + * unless you know what you're doing. */ +extern size_t ___objects_number_allocated; +extern size_t ___objects_number_deallocated; +extern size_t ___objects_number_serialized; + +/**** Function Prototypes ****************************************************/ + +/* Returns the number of hash tables, map tables, lists, + * and sparse arrays allocated thus far. */ +size_t +_objects_number_allocated(void); + +/* Returns the number of hash tables, map tables, lists, + * and sparse arrays deallocated thus far. */ +size_t +_objects_number_deallocated(void); + +/* Returns (but does not increment) the number of hash tables, + * map tables, lists, and sparse arrays given serial numbers thus far. */ +size_t +_objects_number_serialized(void); + +/* Returns the least power of two strictly greater than BOUND. */ +size_t +_objects_next_power_of_two(size_t bound); + +#endif /* __numbers_h_OBJECTS_INCLUDE */ + diff --git a/Headers/gnustep/base/o_x_bas.h.in b/Headers/gnustep/base/o_x_bas.h.in new file mode 100644 index 000000000..52998211b --- /dev/null +++ b/Headers/gnustep/base/o_x_bas.h.in @@ -0,0 +1,126 @@ +/* Basic functions for @XX@ structures. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Mon Dec 11 01:24:48 EST 1995 + * Updated: Mon Mar 11 00:54:50 EST 1996 + * Serial: 96.03.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. */ + +#ifndef __@XX@_bas_h_OBJECTS_INCLUDE +#define __@XX@_bas_h_OBJECTS_INCLUDE 1 + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +#define __@XX@__ 1 + +/**** Function Implementations ***********************************************/ + +/** Magic numbers... **/ + +/* Returns XX's magic number. */ +int +objects_@XX@_magic_number(objects_@XX@_t *xx); + +/** Zones... **/ + +/* Returns the allocs used to create and maintain XX. */ +NSZone * +objects_@XX@_zone(objects_@XX@_t *xx); + +/** Names... **/ + +/* Returns the name that was given to XX. */ +NSString * +objects_@XX@_name(objects_@XX@_t *xx); + +/* Gives XX a name. */ +void +objects_@XX@_set_name(objects_@XX@_t *xx, NSString *name); + +/* Takes away XX's name. */ +void +objects_@XX@_unset_name(objects_@XX@_t *xx); + +/** Serial numbers... **/ + +/* Returns the (process-wide) unique number given to the Libfn + * structure XX. See for more info. */ +size_t +objects_@XX@_serial_number(objects_@XX@_t *xx); + +/* 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); + +/** 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); + +/* Returns the callbacks associated with XX's ``extra''. */ +objects_callbacks_t +objects_@XX@_extra_callbacks(objects_@XX@_t *xx); + +/* Returns XX's ``extra'', a little extra space that each + * structure carries around with it. Its use is + * implementation-dependent. */ +const void * +objects_@XX@_extra(objects_@XX@_t *xx); + +/* Sets XX's ``extra'', a little extra space that each structure + * carries around with it. Its use is implementation-dependent. */ +const void * +objects_@XX@_set_extra(objects_@XX@_t *xx, const void *extra); + +/* Resets XX's ``extra''. */ +void +objects_@XX@_unset_extra(objects_@XX@_t *xx); + +/** Low-level Creation and Destruction **/ + +/* Handles the universal, low-level allocation of structures. */ +objects_@XX@_t * +_objects_@XX@_alloc_with_zone(NSZone *zone); + +/* Handles the universal, low-level deallocation of structures. */ +void +_objects_@XX@_dealloc(objects_@XX@_t *xx); + +/* Handles the low-level copying of structures. */ +objects_@XX@_t * +_objects_@XX@_copy_with_zone(objects_@XX@_t *xx, NSZone *zone); + +/* Returns a low-level description of XX. */ +NSString * +_objects_@XX@_description(objects_@XX@_t *xx); + +#endif /* __@XX@_bas_h_OBJECTS_INCLUDE */ + diff --git a/Headers/gnustep/base/o_x_cbs.h.in b/Headers/gnustep/base/o_x_cbs.h.in new file mode 100644 index 000000000..cf6c9f2af --- /dev/null +++ b/Headers/gnustep/base/o_x_cbs.h.in @@ -0,0 +1,69 @@ +/* Getting callbacks from @YY@ structures. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Mon Dec 11 03:41:00 EST 1995 + * Updated: Mon Mar 11 00:54:20 EST 1996 + * Serial: 96.03.11.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. */ + +#ifndef __@YY@_cbs_h_OBJECTS_INCLUDE +#define __@YY@_cbs_h_OBJECTS_INCLUDE 1 + +/**** 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); + +/* Returns the ``bogus'' marker associated with YY's keys. */ +const void *objects_@YY@_not_a_key_marker (objects_@YY@_t *yy); + +/* Returns the callbacks associated with YY's values. */ +objects_callbacks_t +objects_@YY@_value_callbacks (objects_@YY@_t *yy); + +/* Returns the ``bogus'' marker associated with YY's values. */ +const void *objects_@YY@_not_a_value_marker (objects_@YY@_t *yy); + +#else /* !__map__ */ + +/** Callbacks **/ + +/* Returns the callbacks associated with YY's elements. */ +objects_callbacks_t objects_@YY@_element_callbacks (objects_@YY@_t *yy); + +/* Returns the ``bogus'' marker associated with YY's elements. */ +const void *objects_@YY@_not_an_element_marker (objects_@YY@_t *yy); + +#endif /* __map__ */ + +#endif /* __@YY@_cbs_h_OBJECTS_INCLUDE */ + diff --git a/Source/array.m b/Source/array.m new file mode 100644 index 000000000..d77d58708 --- /dev/null +++ b/Source/array.m @@ -0,0 +1,910 @@ +/* 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: Tue Mar 12 02:42:33 EST 1996 + * Serial: 96.03.12.13 + * + * 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 **/ + +static inline size_t +_objects_array_fold_index(size_t index, size_t slot_count) +{ + return (slot_count ? (index % slot_count) : 0); +} + +static inline size_t +_objects_array_internal_index(objects_array_t *array, size_t index) +{ + return _objects_array_fold_index (index, array->slot_count); +} + +static inline 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)); +} + +static inline 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 != 0 && 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 0; +} + +static inline objects_array_bucket_t * +_objects_array_new_bucket (objects_array_t *array, size_t index, const void *element) +{ + objects_array_bucket_t *bucket; + + bucket = (objects_array_bucket_t *) NSZoneMalloc(objects_array_zone(array), + sizeof(objects_array_bucket_t)); + if (bucket != 0) + { + objects_retain(objects_array_element_callbacks(array), element, array); + bucket->index = index; + bucket->element = element; + } + return bucket; +} + +static inline void +_objects_array_free_bucket(objects_array_t *array, + objects_array_bucket_t *bucket) +{ + if (bucket != 0) + { + objects_release(objects_array_element_callbacks (array), + (void *)(bucket->element), + array); + NSZoneFree(objects_array_zone(array), bucket); + } + + return; +} + +static inline objects_array_slot_t * +_objects_array_new_slots(objects_array_t *array, size_t slot_count) +{ + return (objects_array_slot_t *) NSZoneCalloc(objects_array_zone(array), + slot_count, + sizeof(objects_array_slot_t)); +} + +static inline void +_objects_array_free_slots(objects_array_t *array, + objects_array_slot_t *slots) +{ + if (slots != 0) + NSZoneFree(objects_array_zone(array), slots); + return; +} + +static inline void +_objects_array_empty_slot (objects_array_t *array, objects_array_slot_t * slot) +{ + if (*slot != 0) + { + /* Get rid of the bucket. */ + _objects_array_free_bucket (array, *slot); + + /* Mark the slot as empty. */ + *slot = 0; + + /* Keep the element count accurate */ + --(array->element_count); + } + + /* And return. */ + return; +} + +static inline 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 != 0) + { + _objects_array_free_slots (array, array->sorted_slots); + array->sorted_slots = 0; + } + + if ((*slot) == 0) + { + /* 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 == 0) + /* FIXME: Make this a *little* more friendly. */ + 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] != 0) + { + size_t i; + + i = _objects_array_fold_index (array->slots[d]->index, + new_slot_count); + + if (new_slots[i] == 0) + { + 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 != 0) + _objects_array_free_slots (array, array->slots); + + array->slots = new_slots; + array->slot_count = new_slot_count; + ++(array->element_count); + + return; + } +} + +static inline int +_objects_array_compare_slots (const objects_array_slot_t *slot1, + const objects_array_slot_t *slot2) +{ + if (slot1 == slot2) + return 0; + if (*slot1 == 0) + return 1; + if (*slot2 == 0) + 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 *); + +static inline 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 != 0) + 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; +} + +static inline 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 == 0) + return 0; + + 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 0; + } + else + { + if (enumerator->array->sorted_slots == 0) + return 0; + + if (enumerator->index > 0) + { + objects_array_bucket_t *bucket; + + --(enumerator->index); + bucket = enumerator->array->sorted_slots[enumerator->index]; + return bucket; + } + else + return 0; + } + } + else + { + objects_array_bucket_t *bucket; + + if (enumerator->array->slots == 0) + return 0; + + for (bucket = 0; + (enumerator->index < enumerator->array->slot_count + && bucket == 0); + ++(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) +{ + /* FIXME: Code this. */ + 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_zone(NSZone *zone) +{ + objects_array_t *array; + + /* Get a new array. */ + array = _objects_array_alloc_with_zone(zone); + + return array; +} + +objects_array_t * +objects_array_alloc(void) +{ + return objects_array_alloc_with_zone(0); +} + +objects_array_t * +objects_array_with_zone(NSZone *zone) +{ + return objects_array_init(objects_array_alloc_with_zone(zone)); +} + +objects_array_t * +objects_array_with_zone_with_callbacks(NSZone *zone, + objects_callbacks_t callbacks) +{ + return objects_array_init_with_callbacks(objects_array_alloc_with_zone(zone), + 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_non_owned_void_p(void) +{ + return objects_array_with_callbacks(objects_callbacks_for_non_owned_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 != 0) + { + /* 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 = 0; + } + + 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; + const 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 != 0) + { + /* 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 **/ + +const 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 != 0) + return bucket->element; + else + /* If `bucket' is 0, 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, const void *element) +{ + size_t i; + + for (i = 0; i < array->slot_count; ++i) + { + objects_array_bucket_t *bucket = array->slots[i]; + + if (bucket != 0) + 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, const void *element) +{ + /* Note that this search is quite inefficient. */ + return objects_array_index_of_element (array, element) < (array->slot_count); +} + +const void ** +objects_array_all_elements (objects_array_t *array) +{ + objects_array_enumerator_t enumerator; + const void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (const void **)NSZoneCalloc(objects_array_zone(array), + count + 1, + sizeof(const 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; +} + +const void ** +objects_array_all_elements_ascending (objects_array_t *array) +{ + objects_array_enumerator_t enumerator; + const void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (const void **)NSZoneCalloc(objects_array_zone(array), + count + 1, + sizeof(const 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; +} + +const void ** +objects_array_all_elements_descending (objects_array_t *array) +{ + objects_array_enumerator_t enumerator; + const void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (const void **)NSZoneCalloc(objects_array_zone(array), + count + 1, + sizeof(const 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 != 0) + _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, + const 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, const void *element) +{ + if (objects_array_contains_element (array, element)) + objects_array_remove_element_known_present (array, element); + + return; +} + +/** Adding **/ + +const void * +objects_array_at_index_put_element (objects_array_t *array, + size_t index, + const 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, + const void **element) +{ + objects_array_bucket_t *bucket; + + bucket = _objects_array_enumerator_next_bucket (enumerator); + + if (bucket != 0) + { + if (element != 0) + *element = bucket->element; + if (index != 0) + *index = bucket->index; + return 1; + } + else + { + if (element != 0) + *element = objects_array_not_an_element_marker (enumerator->array); + if (index != 0) + *index = 0; + return 0; + } +} + +int +objects_array_enumerator_next_element (objects_array_enumerator_t * enumerator, + const void **element) +{ + return objects_array_enumerator_next_index_and_element (enumerator, + 0, + 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, + 0); +} + +/** Comparing **/ + +int +objects_array_is_equal_to_array (objects_array_t *array1, objects_array_t *array2) +{ + size_t a, b; + const 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, + const void *(*fcn) (const void *, const void *), + const 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; + const 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; +// const 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/data.m b/Source/data.m new file mode 100644 index 000000000..8a917dc11 --- /dev/null +++ b/Source/data.m @@ -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.m b/Source/hash.m new file mode 100644 index 000000000..7d8afcbc9 --- /dev/null +++ b/Source/hash.m @@ -0,0 +1,1130 @@ +/* A hash table. + * Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: ??? ??? ?? ??:??:?? ??? 1993 + * Updated: Tue Mar 19 00:25:18 EST 1996 + * Serial: 96.03.19.33 + * + * 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 ***********************************************/ + +/** Behind-the-Scenes functions **/ + +static inline objects_hash_bucket_t * +_objects_hash_pick_bucket_for_element(objects_hash_t *hash, + objects_hash_bucket_t *buckets, + size_t bucket_count, + const void *element) +{ + return buckets + (objects_hash(objects_hash_element_callbacks(hash), + element, hash) + % bucket_count); +} + +static inline 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); +} + +static inline objects_hash_bucket_t * +_objects_hash_bucket_for_element(objects_hash_t *hash, const void *element) +{ + return _objects_hash_pick_bucket_for_element(hash, hash->buckets, + hash->bucket_count, element); +} + +static inline 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); +} + +static inline void +_objects_hash_link_node_into_bucket(objects_hash_bucket_t *bucket, + objects_hash_node_t *node) +{ + if (bucket->first_node != 0) + bucket->first_node->prev_in_bucket = node; + + node->next_in_bucket = bucket->first_node; + + bucket->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_bucket->next_in_bucket = node->next_in_bucket; + if (node->next_in_bucket != 0) + node->next_in_bucket->prev_in_bucket = node->prev_in_bucket; + + node->prev_in_bucket = node->next_in_bucket = 0; + + return; +} + +static inline void +_objects_hash_link_node_into_hash(objects_hash_t *hash, + objects_hash_node_t *node) +{ + if (hash->first_node != 0) + hash->first_node->prev_in_hash = node; + node->next_in_hash = hash->first_node; + hash->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_hash->next_in_hash = node->next_in_hash; + if (node->next_in_hash != 0) + node->next_in_hash->prev_in_hash = node->prev_in_hash; + + node->prev_in_hash = node->next_in_hash = 0; + + return; +} + +static inline void +_objects_hash_add_node_to_bucket(objects_hash_bucket_t *bucket, + objects_hash_node_t *node) +{ + if (bucket != 0) + { + _objects_hash_link_node_into_bucket(bucket, node); + + node->bucket = bucket; + + bucket->node_count += 1; + bucket->element_count += 1; + } + + return; +} + +static inline 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; +} + +static inline void +_objects_hash_add_node_to_hash(objects_hash_t *hash, objects_hash_node_t *node) +{ + if (hash != 0) + { + _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; +} + +static inline void +_objects_hash_remove_node_from_its_bucket(objects_hash_node_t *node) +{ + if (node->bucket != 0) + { + node->bucket->node_count -= 1; + node->bucket->element_count -= 1; + + _objects_hash_unlink_node_from_its_bucket(node); + } + + return; +} + +static inline void +_objects_hash_remove_node_from_its_hash(objects_hash_node_t *node) +{ + if (node->hash != 0) + { + 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; +} + +static inline objects_hash_bucket_t * +_objects_hash_new_buckets(objects_hash_t *hash, size_t bucket_count) +{ + return (objects_hash_bucket_t *)NSZoneCalloc(objects_hash_zone(hash), + bucket_count, + sizeof(objects_hash_bucket_t)); +} + +static inline void +_objects_hash_free_buckets(objects_hash_t *hash, objects_hash_bucket_t *buckets) +{ + if (buckets != 0) + NSZoneFree(objects_hash_zone(hash), buckets); + return; +} + +static inline 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) != 0) + { + _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; +} + +static inline objects_hash_node_t * +_objects_hash_new_node(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + /* Allocate the space for a new node. */ + node = (objects_hash_node_t *)NSZoneMalloc(objects_hash_zone(hash), + sizeof(objects_hash_node_t)); + + if (node != 0) + { + /* Retain ELEMENT. (It's released in `_objects_hash_free_node()'.) */ + objects_retain(objects_hash_element_callbacks(hash), element, hash); + + /* Remember ELEMENT. */ + node->element = element; + + /* Associate NODE with HASH. */ + node->hash = hash; + + /* Zero out the various pointers. */ + node->bucket = 0; + node->next_in_bucket = 0; + node->next_in_hash = 0; + node->prev_in_bucket = 0; + node->prev_in_hash = 0; + } + + return node; +} + +static inline void +_objects_hash_free_node(objects_hash_node_t *node) +{ + if (node != 0) + { + 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), + (void *)node->element, + hash); + + /* Actually free the space hash aside for NODE. */ + NSZoneFree(objects_hash_zone(hash), node); + } + + /* And just return. */ + return; +} + +static inline objects_hash_node_t * +_objects_hash_node_for_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node = 0; + + if (element != objects_hash_not_an_element_marker(hash)) + { + objects_hash_bucket_t *bucket = 0; + + /* 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 != 0) && !objects_is_equal(objects_hash_element_callbacks(hash), + element, + node->element, + hash); + node = node->next_in_bucket); + } + + /* Note that if ELEMENT is bogus or if none of the nodes' + * elements matches ELEMENT, then we naturally return 0. */ + return node; +} + +static inline 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 != 0) + enumerator->node = enumerator->node->next_in_hash; + + /* Send back NODE. */ + return node; +} + +/** Callbacks... **/ + +/* Return a hash index for HASH. Needed for the callbacks below. */ +size_t +_objects_hash_hash(objects_hash_t *hash) +{ + /* One might be tempted to do something simple here, but remember: + * If two hash tables are equal they *must* hash to the same value! */ + + /* FIXME: Code this. */ + return 0; +} + +/* An (inefficient, but necessary) "retaining" function for hash tables. */ +objects_hash_t * +_objects_hash_retain(objects_hash_t *hash, objects_hash_t *in_hash) +{ + /* Note that this works only because all the structures (hash, map + * list, array) look alike at first...so we can get the zone of + * one just like we can get the zone of any of them. */ + return objects_hash_copy_with_zone(hash, objects_hash_zone(in_hash)); +} + +/* Returns a collection of callbacks for use with hash tables. */ +objects_callbacks_t +objects_callbacks_for_hash(void) +{ + objects_callbacks_t hash_callbacks = + { + (objects_hash_func_t) _objects_hash_hash, + (objects_compare_func_t) 0, + (objects_is_equal_func_t) objects_hash_is_equal_to_hash, + (objects_retain_func_t) _objects_hash_retain, + (objects_release_func_t) objects_hash_dealloc, + (objects_describe_func_t) objects_hash_description, + 0 + }; + + return hash_callbacks; +} + +/** 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 != 0) + { + _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, const void *element) +{ + objects_hash_node_t *node; + node = _objects_hash_node_for_element(hash, element); + + return node != 0; +} + +const void * +objects_hash_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + /* Try and find the node for ELEMENT. */ + node = _objects_hash_node_for_element(hash, element); + + if (node != 0) + return node->element; + else + return objects_hash_not_an_element_marker(hash); +} + +const void ** +objects_hash_all_elements(objects_hash_t *hash) +{ + size_t j; + const void **array; + objects_hash_enumerator_t enumerator; + + /* FIXME: It probably shouldn't be the programmer's responsibility to + * worry about freeing ARRAY. Maybe we should be returning an NSArray? */ + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_hash_zone(HASH), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_hash_zone(hash), + hash->node_count + 1, + sizeof(void *)); + + /* ENUMERATOR is an enumerator for HASH. */ + enumerator = objects_hash_enumerator_for_hash(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_for_hash()' 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_for_hash(hash); + * objects_hash_node_t *node; + * + * while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + * { + * _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_for_hash(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, + const 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 != 0) + { + /* 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-`0'. */ + if (element != 0) + *element = node->element; + + /* Since we weren't at the end of our enumeration, we return ``true''. */ + return 1; + } + else /* (node == 0) */ + { + /* If NODE isn't real, then we return the bogus element indicator and + * a zero count. */ + if (element != 0) + *element = objects_hash_not_an_element_marker(enumerator->hash); + + /* Since we're at the end of the enumeration, we return ``false''. */ + return 0; + } +} +/** Adding... **/ + +inline const void * +objects_hash_add_element_known_absent(objects_hash_t *hash, + const void *element) +{ + /* Note that we *do not* use the callback functions to test for + * the presence of the bogus element. Is is perfectly permissible for + * elements which are "equal" (but not equal) to the "not an + * element marker" to be added to HASH. */ + if (element == objects_hash_not_an_element_marker(hash)) + { + /* FIXME: We should do something useful here, + * like raise an exception. */ + abort(); + } + else if ((_objects_hash_node_for_element(hash, element)) != 0) + { + /* FIXME: We should do something useful here, + * like raise an exception. */ + abort(); + } + else /* (element != bogus && !(element in hash)) */ + { + objects_hash_node_t *node; + node = _objects_hash_new_node(hash, element); + + if (node != 0) + { + /* Actually add NODE to HASH. */ + _objects_hash_add_node_to_hash(hash, node); + + return node->element; + } + else /* (node == 0) */ + return objects_hash_not_an_element_marker(hash); + } +} + +const void * +objects_hash_add_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + + /* First, we check for ELEMENT in HASH. */ + node = _objects_hash_node_for_element(hash, element); + + if (node == 0) + { + /* ELEMENT isn't in HASH, so we can add it with impunity. */ + return objects_hash_add_element_known_absent(hash, element); + } + else /* (node != 0) */ + { + /* Remember: First retain, then release. */ + objects_retain(objects_hash_element_callbacks(hash), element, hash); + objects_release(objects_hash_element_callbacks(hash), + (void *)(node->element), + hash); + return node->element = element; + } +} + +/* If (any item "equal" to) ELEMENT is in HASH, then that member of HASH is + * returned. Otherwise, the "not an element marker" for HASH is returned + * and ELEMENT is added to HASH. */ +const void * +objects_hash_add_element_if_absent(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + + /* First, we check for ELEMENT in HASH. */ + node = _objects_hash_node_for_element(hash, element); + + if (node == 0) + { + /* ELEMENT isn't in HASH, so we can add it with impunity. */ + objects_hash_add_element_known_absent(hash, element); + + /* To indicate that ELEMENT was not in HASH, we return the bogus + * element indicator. */ + return objects_hash_not_an_element_marker(hash); + } + else /* (node != 0) */ + return node->element; +} +/** Removing **/ + +void +objects_hash_remove_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + node = _objects_hash_node_for_element(hash, element); + + if (node != 0) + { + /* 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_for_hash(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)) != 0) + { + _objects_hash_remove_node_from_its_hash(node); + _objects_hash_free_node(node); + } + + /* And return. */ + return; +} +/** Creating **/ + +objects_hash_t * +objects_hash_alloc_with_zone(NSZone * zone) +{ + objects_hash_t *hash; + /* Get a new hash, using basic methods. */ + hash = _objects_hash_alloc_with_zone(zone); + + return hash; +} + +objects_hash_t * +objects_hash_alloc(void) +{ + return objects_hash_alloc_with_zone(0); +} + +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_zone_with_callbacks(NSZone * zone, + objects_callbacks_t callbacks) +{ + return objects_hash_init_with_callbacks(objects_hash_alloc_with_zone(zone), + callbacks); +} + +objects_hash_t * +objects_hash_with_zone(NSZone * zone) +{ + return objects_hash_init(objects_hash_alloc_with_zone(zone)); +} + +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_non_owned_void_p(void) +{ + return objects_hash_with_callbacks(objects_callbacks_for_non_owned_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_int_p(void) +{ + return objects_hash_with_callbacks(objects_callbacks_for_int_p); +} + +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 != 0) + { + size_t capacity = 10; + /* Make a note of the callbacks 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 = 0; + hash->buckets = 0; + + /* 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 != 0) + { + objects_hash_enumerator_t enumerator; + const 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 = 0; + hash->buckets = 0; + + /* 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_for_hash(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... **/ + +/* If (some item "equal" to) ELEMENT is an element of HASH, then ELEMENT is + * substituted for it. (This is rather like the non-existant but perfectly + * reasonable 'objects_hash_add_element_if_present()'.) */ +void +objects_hash_replace_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + + /* Lookup the node for ELEMENT. */ + node = _objects_hash_node_for_element(hash, element); + + if (node != 0) + { + /* 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), + (void *)(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; + const void *element; + enumerator = objects_hash_enumerator_for_hash(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; + const void *element; + /* Get an element enumerator for OTHER_HASH. */ + enumerator = objects_hash_enumerator_for_hash(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 in ZONE. */ +objects_hash_t * +objects_hash_copy_with_zone(objects_hash_t *old_hash, NSZone * zone) +{ + objects_hash_t *new_hash; + /* Alloc the NEW_HASH, copying over the low-level stuff. */ + new_hash = _objects_hash_copy_with_zone(old_hash, zone); + + /* 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 the default zone. */ +objects_hash_t * +objects_hash_copy(objects_hash_t *old_hash) +{ + return objects_hash_copy_with_zone(old_hash, 0); +} + +/** 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, + const void *(*fcn)(const void *, const void *), + const void *user_data) +{ + objects_hash_enumerator_t enumerator; + objects_hash_node_t *node; + enumerator = objects_hash_enumerator_for_hash(hash); + + while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + { + const 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), + (void *)(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_for_hash(hash); + + while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + 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_for_hash(hash); + + /* FIXME: Make this more efficient by enumerating + * over the smaller of the two hashes only. */ + while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + 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; + const void *element; + + enumerator = objects_hash_enumerator_for_hash(other_hash); + + while (objects_hash_enumerator_next_element(&enumerator, &element)) + objects_hash_add_element_if_absent(hash, element); + + return hash; +} + +/** Describing a hash table... **/ + +NSString * +objects_hash_description(objects_hash_t *hash) +{ +/* FIXME: Fix this. + NSMutableString *string; + NSString *gnirts; + objects_callbacks_t callbacks; + objects_hash_enumerator_t enumerator; + const void *element; + + callbacks = objects_hash_element_callbacks(hash); + enumerator = objects_hash_enumerator_for_hash(hash); + string = [_objects_hash_description(hash) mutableCopy]; + + [[string retain] autorelease]; + +#define DESCRIBE(E) objects_describe(callbacks, (E), hash) + + [string appendFormat:@"element_count = %d;\n", objects_hash_count(hash)]; + [string appendFormat:@"not_an_element_marker = %@;\n", + DESCRIBE(objects_hash_not_an_element_marker(hash))]; + [string appendString:@"elements = {\n"]; + + while (objects_hash_enumerator_next_element(&enumerator, &element)) + [string appendFormat:@"%@,\n", DESCRIBE(element)]; + + [string appendFormat:@"%@};\n", + DESCRIBE(objects_hash_not_an_element_marker(hash))]; + +#undef DESCRIBE + + gnirts = [[[string copy] retain] autorelease]; + + [string release]; + + return gnirts; +*/ + return nil; +} diff --git a/Source/list.m b/Source/list.m new file mode 100644 index 000000000..a018a4596 --- /dev/null +++ b/Source/list.m @@ -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: Wed Mar 20 20:48:39 EST 1996 + * Serial: 96.03.20.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 +#include +#include + +/**** Function Implementations ***********************************************/ + +/** Background functions **/ + +static 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 != 0) + node->next_in_list->prev_in_list = node->prev_in_list; + if (node->prev_in_list != 0) + node->prev_in_list->next_in_list = node->next_in_list; + + node->list->node_count -= 1; + node->list->element_count -= 1; + + return; +} + +static inline objects_list_node_t * +_objects_list_new_node (objects_list_t *list, const void *element) +{ + objects_list_node_t *node; + + node = NSZoneMalloc(objects_list_zone(list), sizeof(objects_list_node_t)); + + if (node != 0) + { + node->list = list; + node->next_in_list = 0; + node->prev_in_list = 0; + 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), + (void*)node->element, + node->list); + NSZoneFree(objects_list_zone(list), node); + return; +} + +static 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 != 0 && n != 0) + { + node = node->prev_in_list; + ++n; + } + } + else + /* (n >= 0) */ + { + node = list->first_node; + + while (node != 0 && n != 0) + { + node = node->next_in_list; + --n; + } + } + + return node; +} + +static inline objects_list_node_t * +_objects_list_nth_node_for_element (objects_list_t *list, + long int n, + const void *element) +{ + objects_list_node_t *node; + + if (n < 0) + { + node = list->last_node; + + ++n; + + while (node != 0 && 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 != 0 && 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; +} + +static 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 != 0) + { + 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, const void *element) +{ + objects_list_enumerator_t enumerator; + const 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; + + /* 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, + const 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 != 0) + { + /* If NODE is real, then return the element it contains. */ + if (element != 0) + *element = node->element; + + /* Indicate that the enumeration continues. */ + return 1; + } + else + { + /* If NODE isn't real, then we return the ``bogus'' indicator. */ + if (element != 0) + *element = objects_list_not_an_element_marker (enumerator->list); + + /* Indicate that the enumeration is over. */ + return 0; + } +} + +/** Searching **/ + +const void * +objects_list_element (objects_list_t *list, const void *element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, 0, element); + + if (node != 0) + return node->element; + else + return objects_list_not_an_element_marker (list); +} + +const 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 != 0) + return node->element; + else + return objects_list_not_an_element_marker (list); +} + +const void * +objects_list_first_element (objects_list_t *list) +{ + if (list->first_node != 0) + return list->first_node->element; + else + return objects_list_not_an_element_marker (list); +} + +const void * +objects_list_last_element (objects_list_t *list) +{ + if (list->last_node != 0) + return list->last_node->element; + else + return objects_list_not_an_element_marker (list); +} + +/** Obtaining elements **/ + +const void ** +objects_list_all_elements (objects_list_t *list) +{ + objects_list_enumerator_t enumerator; + const void **array; + size_t i; + + array = NSZoneCalloc(objects_list_zone(list), + objects_list_count(list) + 1, + sizeof(const void *)); + + for (i = 0; objects_list_enumerator_next_element (&enumerator, array + i); ++i); + + return array; +} + +/** Adding elements **/ + +const void * +objects_list_append_element (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element (list, -1, element); +} + +const void * +objects_list_append_element_if_absent (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element_if_absent (list, -1, element); +} + +const void * +objects_list_prepend_element (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element (list, 0, element); +} + +const void * +objects_list_prepend_element_if_absent (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element_if_absent (list, 0, element); +} + +const void * +objects_list_at_index_insert_element(objects_list_t *list, + long int n, + const 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 == 0) + /* FIXME: Make this a *little* more graceful, for goodness' sake! */ + abort(); + + if (n < 0) + { + if (node == 0) + { + anode = 0; + bnode = list->first_node; + } + else + /* (node != 0) */ + { + anode = node; + bnode = node->next_in_list; + } + } + else + /* (n >= 0) */ + { + if (node == 0) + { + anode = list->last_node; + bnode = 0; + } + else + /* (node != 0) */ + { + anode = node->prev_in_list; + bnode = node; + } + } + + new_node->prev_in_list = anode; + new_node->next_in_list = bnode; + + if (anode != 0) + anode->next_in_list = new_node; + if (bnode != 0) + 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; +} + +const void * +objects_list_at_index_insert_element_if_absent (objects_list_t *list, + long int n, + const 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, + const void *element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, n, element); + + if (node != 0) + { + _objects_list_remove_node_from_its_list (node); + _objects_list_free_node (list, node); + } + + return; +} + +void +objects_list_remove_element (objects_list_t *list, const void *element) +{ + objects_list_remove_nth_occurrance_of_element (list, 0, element); + return; +} + +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 != 0) + { + _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)) != 0) + { + _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, + const void *old_element, + const void *new_element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, n, old_element); + + if (node != 0) + { + objects_retain (objects_list_element_callbacks (list), new_element, list); + objects_release (objects_list_element_callbacks (list), + (void*)node->element, + list); + node->element = new_element; + } + + return; +} + +void +objects_list_replace_element (objects_list_t *list, + const void *old_element, + const 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, + const void *new_element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node (list, n); + + if (node != 0) + { + objects_retain (objects_list_element_callbacks (list), new_element, list); + objects_release (objects_list_element_callbacks (list), + (void*)node->element, + list); + node->element = new_element; + } + + return; +} + +void +objects_list_replace_first_element (objects_list_t *list, + const void *new_element) +{ + objects_list_replace_nth_element (list, 0, new_element); + return; +} + +void +objects_list_replace_last_element (objects_list_t *list, + const void *new_element) +{ + objects_list_replace_nth_element (list, -1, new_element); + return; +} + +/** Creating **/ + +objects_list_t * +objects_list_alloc_with_zone (NSZone *zone) +{ + objects_list_t *list; + + list = _objects_list_alloc_with_zone(zone); + + return list; +} + +objects_list_t * +objects_list_alloc (void) +{ + return objects_list_alloc_with_zone (0); +} + +objects_list_t * +objects_list (void) +{ + return objects_list_init (objects_list_alloc ()); +} + +objects_list_t * +objects_list_with_zone (NSZone *zone) +{ + return objects_list_init (objects_list_alloc_with_zone(zone)); +} + +objects_list_t * +objects_list_with_zone_with_callbacks (NSZone *zone, + objects_callbacks_t callbacks) +{ + return objects_list_init_with_callbacks(objects_list_alloc_with_zone(zone), + 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_non_owned_void_p (void) +{ + return objects_list_with_callbacks (objects_callbacks_for_non_owned_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 != 0) + { + list->callbacks = callbacks; + list->element_count = 0; + list->node_count = 0; + list->first_node = 0; + list->last_node = 0; + } + + return list; +} + +objects_list_t * +objects_list_init_from_list (objects_list_t *list, objects_list_t *old_list) +{ + objects_list_enumerator_t enumerator; + const void *element; + + if (list != 0) + { + list->callbacks = objects_list_element_callbacks (old_list); + list->element_count = 0; + list->node_count = 0; + list->first_node = 0; + list->last_node = 0; + + if (old_list != 0) + { + /* 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; + const 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) +{ + return objects_list_copy_with_zone (old_list, 0); +} + +objects_list_t * +objects_list_copy_with_zone (objects_list_t *old_list, NSZone *zone) +{ + objects_list_t *list; + + /* Allocate a new (low-level) copy of OLD_LIST. */ + list = _objects_list_copy_with_zone(old_list, zone); + + /* Fill it in. */ + return objects_list_init_from_list (list, old_list); +} + +/** Mapping **/ + +objects_list_t * +objects_list_map_elements(objects_list_t *list, + const void *(*fcn)(const void *, void *), + void *user_data) +{ + objects_list_enumerator_t enumerator; + objects_list_node_t *node; + objects_callbacks_t callbacks; + + callbacks = objects_list_element_callbacks(list); + enumerator = objects_list_enumerator (list); + + while ((node = _objects_list_enumerator_next_node (&enumerator)) != 0) + { + const void *element; + + element = (*fcn)(node->element, user_data); + + objects_retain (callbacks, element, list); + objects_release (callbacks, (void *)(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 != 0) + { + objects_list_enumerator_t enumerator; + const 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 = 0; + hash->buckets = 0; + + /* 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 != 0) +// { +// objects_list_enumerator_t enumerator; +// const 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 = 0; +// chash->buckets = 0; +// +// /* 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.m b/Source/map.m new file mode 100644 index 000000000..18576513a --- /dev/null +++ b/Source/map.m @@ -0,0 +1,1379 @@ +/* A map table implementation. + * Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: ??? ??? ?? ??:??:?? ??? 1993 + * Updated: Tue Mar 12 02:12:37 EST 1996 + * Serial: 96.03.12.25 + * + * 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 **/ + +static inline objects_map_bucket_t * +_objects_map_pick_bucket_for_key(objects_map_t *map, + objects_map_bucket_t *buckets, + size_t bucket_count, + const void *key) +{ + return buckets + (objects_hash(objects_map_key_callbacks(map), + key, map) + % bucket_count); +} + +static inline 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); +} + +static inline objects_map_bucket_t * +_objects_map_bucket_for_key(objects_map_t *map, const void *key) +{ + return _objects_map_pick_bucket_for_key(map, map->buckets, + map->bucket_count, key); +} + +static inline 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); +} + +static inline void +_objects_map_link_node_into_bucket(objects_map_bucket_t *bucket, + objects_map_node_t *node) +{ + if (bucket->first_node != 0) + bucket->first_node->prev_in_bucket = node; + + node->next_in_bucket = bucket->first_node; + + bucket->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_bucket->next_in_bucket = node->next_in_bucket; + if (node->next_in_bucket != 0) + node->next_in_bucket->prev_in_bucket = node->prev_in_bucket; + + node->prev_in_bucket = node->next_in_bucket = 0; + + return; +} + +static inline void +_objects_map_link_node_into_map(objects_map_t *map, + objects_map_node_t *node) +{ + if (map->first_node != 0) + map->first_node->prev_in_map = node; + + node->next_in_map = map->first_node; + + map->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_map->next_in_map = node->next_in_map; + if (node->next_in_map != 0) + node->next_in_map->prev_in_map = node->prev_in_map; + + node->prev_in_map = node->next_in_map = 0; + + return; +} + +static inline void +_objects_map_add_node_to_bucket(objects_map_bucket_t *bucket, + objects_map_node_t *node) +{ + if (bucket != 0) + { + _objects_map_link_node_into_bucket(bucket, node); + + node->bucket = bucket; + + bucket->node_count += 1; + bucket->element_count += 1; + } + + return; +} + +static inline 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; +} + +static inline void +_objects_map_add_node_to_map(objects_map_t *map, objects_map_node_t *node) +{ + if (map != 0) + { + _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; +} + +static inline void +_objects_map_remove_node_from_its_bucket(objects_map_node_t *node) +{ + if (node->bucket != 0) + { + node->bucket->node_count -= 1; + node->bucket->element_count -= 1; + + _objects_map_unlink_node_from_its_bucket(node); + } + + return; +} + +static inline void +_objects_map_remove_node_from_its_map(objects_map_node_t *node) +{ + if (node->map != 0) + { + 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; +} + +static inline objects_map_bucket_t * +_objects_map_new_buckets(objects_map_t *map, size_t bucket_count) +{ + return (objects_map_bucket_t *)NSZoneCalloc(objects_map_zone(map), + bucket_count, + sizeof(objects_map_bucket_t)); +} + +static inline void +_objects_map_free_buckets(objects_map_t *map, objects_map_bucket_t *buckets) +{ + if (buckets != 0) + NSZoneFree(objects_map_zone(map), buckets); + return; +} + +static inline 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) != 0) + { + _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; +} + +static inline objects_map_node_t * +_objects_map_new_node(objects_map_t *map, const void *key, const void *value) +{ + objects_map_node_t *node; + /* Allocate the space for a new node. */ + node = (objects_map_node_t *)NSZoneMalloc(objects_map_zone(map), + sizeof(objects_map_node_t)); + + if (node != 0) + { + /* 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 = 0; + node->bucket = 0; + node->next_in_bucket = 0; + node->next_in_map = 0; + node->prev_in_bucket = 0; + node->prev_in_map = 0; + } + + return node; +} + +static inline void +_objects_map_free_node(objects_map_node_t *node) +{ + if (node != 0) + { + 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), + (void *)node->key, map); + objects_release(objects_map_value_callbacks(map), + (void *)node->value, map); + + /* Actually free the space map aside for NODE. */ + NSZoneFree(objects_map_zone(map), node); + } + + /* And just return. */ + return; +} + +static inline objects_map_node_t * +_objects_map_node_for_key(objects_map_t *map, const 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 != 0) && !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 `0'. */ + return node; +} + +/** Callbacks... **/ + +/* Return a hash index for MAP. Needed for the callbacks below. */ +size_t +_objects_map_hash(objects_map_t *map) +{ + /* One might be tempted to do something simple here, but remember: + * If two map tables are equal they *must* hash to the same value! */ + + /* FIXME: Code this. */ + return 0; +} + +/* An (inefficient, but necessary) "retaining" function for map tables. */ +objects_map_t * +_objects_map_retain(objects_map_t *map, objects_map_t *in_map) +{ + /* Note that this works only because all the structures (hash, map + * list, array) look alike at first...so we can get the zone of + * one just like we can get the zone of any of them. */ + return objects_map_copy_with_zone(map, objects_map_zone(in_map)); +} + +/* Returns a collection of callbacks for use with map tables. */ +objects_callbacks_t +objects_callbacks_for_map(void) +{ + objects_callbacks_t map_callbacks = + { + (objects_hash_func_t) _objects_map_hash, + (objects_compare_func_t) 0, + (objects_is_equal_func_t) objects_map_is_equal_to_map, + (objects_retain_func_t) _objects_map_retain, + (objects_release_func_t) objects_map_dealloc, + (objects_describe_func_t) objects_map_description, + 0 + }; + + return map_callbacks; +} + +/** 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 != 0) + { + _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... **/ + +/* Returns the number of key/value pairs in MAP. */ +size_t +objects_map_count(objects_map_t *map) +{ + return map->element_count; +} + +/* Returns some (inexact) measure of how many key/value pairs + * MAP can comfortably hold without resizing. */ +size_t +objects_map_capacity(objects_map_t *map) +{ + return map->bucket_count; +} + +/* Performs an internal consistency check, returns 'true' if + * everything is OK, 'false' otherwise. Really useful only + * for debugging. */ +int +objects_map_check(objects_map_t *map) +{ + /* FIXME: Code this. */ + return 1; +} + +int +objects_map_is_empty(objects_map_t *map) +{ + return objects_map_count(map) == 0; +} + +/** Searching **/ + +/* Returns 'true' if and only if some key in MAP is equal + * (in the sense of the key callbacks of MAP) to KEY. */ +int +objects_map_contains_key(objects_map_t *map, const void *key) +{ + if (_objects_map_node_for_key(map, key) != 0) + return 1; + else + return 0; +} + +/* Returns 'true' if and only if some value in MAP is equal + * (in the sense of the value callbacks of MAP) to VALUE. */ +/* WARNING: This is rather inefficient. Not to be used lightly. */ +int +objects_map_contains_value(objects_map_t *map, const void *value) +{ + objects_map_enumerator_t me; + const void *v = 0; + + /* ME is an enumerator for MAP. */ + me = objects_map_enumerator_for_map(map); + + /* Enumerate, and check. */ + while (objects_map_enumerator_next_value(&me, &v)) + if (objects_is_equal(objects_map_value_callbacks(map), value, v, map)) + return 1; + + /* If we made it this far, then VALUE isn't in MAP. */ + return 0; +} + +/* If KEY is in MAP, then the following three things happen: + * (1) 'true' is returned; + * (2) if OLD_KEY is non-zero, then the key in MAP + * equal to KEY is placed there; + * (3) if VALUE is non-zero, then the value in MAP + * mapped to by KEY is placed there. + * If KEY is not in MAP, then the following three things happen: + * (1) 'false' is returned; + * (2) if OLD_KEY is non-zero, then the "not a key marker" + * for MAP is placed there; + * (3) if VALUE is non-zero, then the the "not a value marker" + * for MAP is placed there. */ +inline int +objects_map_key_and_value_at_key(objects_map_t *map, + const void **old_key, + const void **value, + const void *key) +{ + objects_map_node_t *node; + + /* Try and find the node for KEY. */ + node = _objects_map_node_for_key(map, key); + + if (node != 0) + { + if (old_key != 0) + *old_key = node->key; + if (value != 0) + *value = node->value; + return 1; + } + else /* (node == 0) */ + { + if (old_key != 0) + *old_key = objects_map_not_a_key_marker(map); + if (value != 0) + *value = objects_map_not_a_value_marker(map); + return 0; + } +} + +/* If KEY is in MAP, then the key of MAP which is equal to KEY + * is returned. Otherwise, the "not a key marker" for MAP is returned. */ +const void * +objects_map_key_at_key(objects_map_t *map, const void *key) +{ + const void *old_key; + + /* Use the grandfather function above... */ + objects_map_key_and_value_at_key(map, &old_key, 0, key); + + return old_key; +} + +/* If KEY is in MAP, then the value of MAP which to which KEY maps + * is returned. Otherwise, the "not a value marker" for MAP is returned. */ +const void * +objects_map_value_at_key(objects_map_t *map, const void *key) +{ + const void *value; + + /* Use the grandfather function above... */ + objects_map_key_and_value_at_key(map, 0, &value, key); + + return value; +} + +const void ** +objects_map_all_keys_and_values(objects_map_t *map) +{ + size_t j; + const void **array; + objects_map_enumerator_t enumerator; + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_map_zone(MAP), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_map_zone(map), + 2 * (objects_map_count(map) + 1), + sizeof(void *)); + + /* ENUMERATOR is an enumerator for MAP. */ + enumerator = objects_map_enumerator_for_map(map); + + /* Now we enumerate through the elements of MAP, 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; +} + +const void ** +objects_map_all_keys(objects_map_t *map) +{ + size_t j; + const void **array; + objects_map_enumerator_t enumerator; + + /* FIXME: Morally, deallocating this space shouldn't be the + * programmer's responsibility. Maybe we should be returning + * an NSArray? */ + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_map_zone(MAP), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_map_zone(map), + objects_map_count(map) + 1, + sizeof(void *)); + + /* ENUMERATOR is an enumerator for MAP. */ + enumerator = objects_map_enumerator_for_map(map); + + /* Now we enumerate through the elements of MAP, 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; +} + +const void ** +objects_map_all_values(objects_map_t *map) +{ + size_t j; + const void **array; + objects_map_enumerator_t enumerator; + + /* FIXME: Morally, deallocating this space shouldn't be the + * programmer's responsibility. Maybe we should be returning + * an NSArray? */ + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_map_zone(MAP), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_map_zone(map), + objects_map_count(map) + 1, + sizeof(void *)); + + /* ENUMERATOR is an enumerator for MAP. */ + enumerator = objects_map_enumerator_for_map(map); + + /* Now we enumerate through the elements of MAP, 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 MAP WHILE YOU'RE ENUMERATING IT. */ + +/* IMPORTANT WARNING: Map 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_for_map()' 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_for_map(map); + * objects_map_node_t *node; + * + * while ((node = _objects_map_next_node(&enumerator)) != 0) + * { + * _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_for_map(objects_map_t *map) +{ + objects_map_enumerator_t enumerator; + /* Make sure ENUMERATOR knows its mapionary. */ + enumerator.map = map; + + /* Start ENUMERATOR at MAP'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 != 0) + 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, + const void **key, + const 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 != 0) + { + /* If NODE is real, then return the key and value it contains. */ + if (key != 0) + *key = node->key; + if (value != 0) + *value = node->value; + + /* Indicate that the enumeration continues. */ + return 1; + } + else + { + /* If NODE isn't real, then we return the ``bogus'' indicators. */ + if (key != 0) + *key = objects_map_not_a_key_marker(enumerator->map); + if (value != 0) + *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, + const void **key) +{ + return objects_map_enumerator_next_key_and_value(enumerator, key, 0); +} + +int +objects_map_enumerator_next_value(objects_map_enumerator_t *enumerator, + const void **value) +{ + return objects_map_enumerator_next_key_and_value(enumerator, 0, value); +} +/** Adding **/ + +/* FIXME: Make this check for invalidity and abort or raise an exception! */ + +const void * +objects_map_at_key_put_value_known_absent(objects_map_t *map, + const void *key, + const 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 != 0) + { + /* 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 `0' 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); + } +} + +const void * +objects_map_at_key_put_value(objects_map_t *map, + const void *key, + const void *value) +{ + objects_map_node_t *node; + /* First, we check for KEY in MAP. */ + node = _objects_map_node_for_key(map, key); + + if (node != 0) + { + objects_retain(objects_map_value_callbacks(map), value, map); + objects_release(objects_map_value_callbacks(map), + (void *)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); + } +} + +const void * +objects_map_at_key_put_value_if_absent(objects_map_t *map, + const void *key, + const 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 != 0) + { + /* 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, const 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 != 0) + { + /* 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_for_map(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)) != 0) + { + _objects_map_remove_node_from_its_map(node); + _objects_map_free_node(node); + } + + /* And return. */ + return; +} +/** Creating **/ + +objects_map_t * +objects_map_alloc_with_zone(NSZone * zone) +{ + objects_map_t *map; + map = _objects_map_alloc_with_zone(zone); + + return map; +} + +objects_map_t * +objects_map_alloc(void) +{ + return objects_map_alloc_with_zone(0); +} + +objects_map_t * +objects_map_with_zone(NSZone * zone) +{ + return objects_map_init(objects_map_alloc_with_zone(zone)); +} + +objects_map_t * +objects_map_with_zone_with_callbacks(NSZone * zone, + objects_callbacks_t key_callbacks, + objects_callbacks_t value_callbacks) +{ + return objects_map_init_with_callbacks(objects_map_alloc_with_zone(zone), + 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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_char_p, + objects_callbacks_for_non_owned_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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_non_owned_void_p, + objects_callbacks_for_non_owned_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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_int, + objects_callbacks_for_non_owned_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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_id, + objects_callbacks_for_non_owned_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 != 0) + { + size_t capacity = 10; + + /* Make a note of the callbacks 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 = 0; + map->buckets = 0; + + /* 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; + const void *key; + const 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_for_map(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... **/ + +/* Releases all the keys and values of MAP, and then + * deallocates MAP itself. */ +void +objects_map_dealloc(objects_map_t *map) +{ + if (map != 0) + { + /* 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, + const void *key) +{ + objects_map_node_t *node; + + /* Look up the node (if any) for KEY in MAP. */ + node = _objects_map_node_for_key(map, key); + + if (node != 0) + { + /* Remember: First retain, then release; + * just in case they're the same. */ + objects_retain(objects_map_key_callbacks(map), key, map); + objects_release(objects_map_key_callbacks(map), + (void *)(node->key), map); + + /* Because "equality" is suppossedly transitive, we needn't + * worry about having duplicate keys in MAP after this. */ + node->key = key; + } + + return; +} +/** Comparing **/ + +/* Returns 'true' if every key/value pair of MAP2 is also a key/value pair + * of MAP1. Otherwise, returns 'false'. */ +int +objects_map_contains_map(objects_map_t *map1, objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key = 0; + const void *value = 0; + + /* ENUMERATOR is a key/value enumerator for MAP2. */ + enumerator = objects_map_enumerator_for_map(map2); + + while (objects_map_enumerator_next_key_and_value(&enumerator, &key, &value)) + { + objects_map_node_t *node; + + /* Try an get MAP1's node for KEY. */ + node = _objects_map_node_for_key(map1, key); + + /* If MAP1 doesn't even have KEY as a key, then we're done. */ + if (node == 0) + return 0; + + /* If MAP1 has K as a key, but doesn't map + * KEY to VALUE, then we're done. */ + if (objects_compare(objects_map_value_callbacks(map1), node->value, + value, map1)) + return 0; + } + + return 1; +} + +/* Returns 'true' iff some key/value pair of MAP1 if also + * a key/value pair of MAP2. */ +int +objects_map_intersects_map(objects_map_t *map1, objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key = 0; + const void *value = 0; + + /* ENUMERATOR is a key/value enumerator for MAP2. */ + enumerator = objects_map_enumerator_for_map(map2); + + while (objects_map_enumerator_next_key_and_value(&enumerator, &key, &value)) + { + objects_map_node_t *node; + + /* Try an get MAP1's node for KEY. */ + node = _objects_map_node_for_key(map1, key); + + /* If MAP1 doesn't even have KEY as a key, then we're done. */ + if (node != 0) + /* If MAP1 has KEY as a key, and maps KEY + * to VALUE, then we're done. Yippee! */ + if (objects_is_equal(objects_map_value_callbacks(map1), + node->value, value, map1)) + return 1; + } + + return 0; +} + +int +objects_map_is_equal_to_map(objects_map_t *map1, objects_map_t *map2) +{ + /* Check the counts. */ + if (objects_map_count(map1) != objects_map_count(map2)) + return 0; + + /* If the counts match, then we do an pair by pair 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; +} + +/* Returns 'true' iff every key of MAP2 is a key of MAP1. */ +int +objects_map_keys_contain_keys_of_map(objects_map_t *map1, objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key; + + enumerator = objects_map_enumerator_for_map(map2); + + if (objects_map_count(map1) < objects_map_count(map2)) + return 0; + + while (objects_map_enumerator_next_key(&enumerator, &key)) + if (!objects_map_contains_key(map1, key)) + return 0; + + return 1; +} + +/* Returns 'true' iff some key of MAP1 if also a key of MAP2. */ +int +objects_map_keys_intersect_keys_of_map(objects_map_t *map1, + objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key; + + enumerator = objects_map_enumerator_for_map(map1); + + while (objects_map_enumerator_next_key(&enumerator, &key)) + if (objects_map_contains_key(map2, key)) + return 1; + + return 0; +} + +/* Returns 'true' if MAP1 and MAP2 have the same number of key/value pairs, + * MAP1 contains every key of MAP2, and MAP2 contains every key of MAP1. + * Otherwise, returns 'false'. */ +int +objects_map_keys_are_equal_to_keys_of_map(objects_map_t *map1, + objects_map_t *map2); + +/** Copying **/ + +objects_map_t * +objects_map_copy_with_zone(objects_map_t *old_map, NSZone * zone) +{ + objects_map_t *new_map; + /* Alloc the NEW_MAP. */ + new_map = _objects_map_copy_with_zone(old_map, zone); + + /* 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) +{ + return objects_map_copy_with_zone(old_map, 0); +} + +/** Describing... **/ + +/* Returns a string describing (the contents of) MAP. */ +NSString * +objects_map_description(objects_map_t *map) +{ + /* FIXME: Code this. */ + return nil; +} + +/** Mapping **/ + +objects_map_t * +objects_map_map_keys(objects_map_t *map, + const void *(*fcn)(const void *, void *), + void *user_data) +{ + objects_map_enumerator_t enumerator; + objects_map_node_t *node; + + enumerator = objects_map_enumerator_for_map(map); + + while ((node = _objects_map_enumerator_next_node(&enumerator)) != 0) + { + const void *key; + + key = (*fcn)(node->key, user_data); + + objects_retain(objects_map_key_callbacks(map), key, map); + objects_release(objects_map_key_callbacks(map), + (void *)(node->key), map); + node->key = key; + } + + return map; +} + +objects_map_t * +objects_map_map_values(objects_map_t *map, + const void *(*fcn) (const void *, void *), + void *user_data) +{ + objects_map_enumerator_t enumerator; + objects_map_node_t *node; + + enumerator = objects_map_enumerator_for_map(map); + + while ((node = _objects_map_enumerator_next_node(&enumerator)) != 0) + { + const void *value; + + value = (*fcn)(node->value, user_data); + + objects_retain(objects_map_value_callbacks(map), value, map); + objects_release(objects_map_value_callbacks(map), + (void *)(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; + const void *key; + + enumerator = objects_map_enumerator_for_map(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; + const void *key; + + enumerator = objects_map_enumerator_for_map(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; + const void *key; + const void *value; + + enumerator = objects_map_enumerator_for_map(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/numbers.c b/Source/numbers.c new file mode 100644 index 000000000..b5a073052 --- /dev/null +++ b/Source/numbers.c @@ -0,0 +1,64 @@ +/* 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: Mon Mar 11 02:40:00 EST 1996 + * Serial: 96.03.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 + +/**** Type, Constant, and Macro Definitions **********************************/ + +size_t ___objects_number_allocated = 0; +size_t ___objects_number_deallocated = 0; +size_t ___objects_number_serialized = 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_serialized(void) +{ + return ___objects_number_serialized; +} + +size_t +_objects_next_power_of_two(size_t bound) +{ + size_t start = 1; + while ((start <= bound) && (start <<= 1)); + return start; +} diff --git a/Source/o_array.m b/Source/o_array.m new file mode 100644 index 000000000..d77d58708 --- /dev/null +++ b/Source/o_array.m @@ -0,0 +1,910 @@ +/* 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: Tue Mar 12 02:42:33 EST 1996 + * Serial: 96.03.12.13 + * + * 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 **/ + +static inline size_t +_objects_array_fold_index(size_t index, size_t slot_count) +{ + return (slot_count ? (index % slot_count) : 0); +} + +static inline size_t +_objects_array_internal_index(objects_array_t *array, size_t index) +{ + return _objects_array_fold_index (index, array->slot_count); +} + +static inline 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)); +} + +static inline 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 != 0 && 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 0; +} + +static inline objects_array_bucket_t * +_objects_array_new_bucket (objects_array_t *array, size_t index, const void *element) +{ + objects_array_bucket_t *bucket; + + bucket = (objects_array_bucket_t *) NSZoneMalloc(objects_array_zone(array), + sizeof(objects_array_bucket_t)); + if (bucket != 0) + { + objects_retain(objects_array_element_callbacks(array), element, array); + bucket->index = index; + bucket->element = element; + } + return bucket; +} + +static inline void +_objects_array_free_bucket(objects_array_t *array, + objects_array_bucket_t *bucket) +{ + if (bucket != 0) + { + objects_release(objects_array_element_callbacks (array), + (void *)(bucket->element), + array); + NSZoneFree(objects_array_zone(array), bucket); + } + + return; +} + +static inline objects_array_slot_t * +_objects_array_new_slots(objects_array_t *array, size_t slot_count) +{ + return (objects_array_slot_t *) NSZoneCalloc(objects_array_zone(array), + slot_count, + sizeof(objects_array_slot_t)); +} + +static inline void +_objects_array_free_slots(objects_array_t *array, + objects_array_slot_t *slots) +{ + if (slots != 0) + NSZoneFree(objects_array_zone(array), slots); + return; +} + +static inline void +_objects_array_empty_slot (objects_array_t *array, objects_array_slot_t * slot) +{ + if (*slot != 0) + { + /* Get rid of the bucket. */ + _objects_array_free_bucket (array, *slot); + + /* Mark the slot as empty. */ + *slot = 0; + + /* Keep the element count accurate */ + --(array->element_count); + } + + /* And return. */ + return; +} + +static inline 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 != 0) + { + _objects_array_free_slots (array, array->sorted_slots); + array->sorted_slots = 0; + } + + if ((*slot) == 0) + { + /* 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 == 0) + /* FIXME: Make this a *little* more friendly. */ + 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] != 0) + { + size_t i; + + i = _objects_array_fold_index (array->slots[d]->index, + new_slot_count); + + if (new_slots[i] == 0) + { + 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 != 0) + _objects_array_free_slots (array, array->slots); + + array->slots = new_slots; + array->slot_count = new_slot_count; + ++(array->element_count); + + return; + } +} + +static inline int +_objects_array_compare_slots (const objects_array_slot_t *slot1, + const objects_array_slot_t *slot2) +{ + if (slot1 == slot2) + return 0; + if (*slot1 == 0) + return 1; + if (*slot2 == 0) + 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 *); + +static inline 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 != 0) + 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; +} + +static inline 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 == 0) + return 0; + + 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 0; + } + else + { + if (enumerator->array->sorted_slots == 0) + return 0; + + if (enumerator->index > 0) + { + objects_array_bucket_t *bucket; + + --(enumerator->index); + bucket = enumerator->array->sorted_slots[enumerator->index]; + return bucket; + } + else + return 0; + } + } + else + { + objects_array_bucket_t *bucket; + + if (enumerator->array->slots == 0) + return 0; + + for (bucket = 0; + (enumerator->index < enumerator->array->slot_count + && bucket == 0); + ++(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) +{ + /* FIXME: Code this. */ + 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_zone(NSZone *zone) +{ + objects_array_t *array; + + /* Get a new array. */ + array = _objects_array_alloc_with_zone(zone); + + return array; +} + +objects_array_t * +objects_array_alloc(void) +{ + return objects_array_alloc_with_zone(0); +} + +objects_array_t * +objects_array_with_zone(NSZone *zone) +{ + return objects_array_init(objects_array_alloc_with_zone(zone)); +} + +objects_array_t * +objects_array_with_zone_with_callbacks(NSZone *zone, + objects_callbacks_t callbacks) +{ + return objects_array_init_with_callbacks(objects_array_alloc_with_zone(zone), + 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_non_owned_void_p(void) +{ + return objects_array_with_callbacks(objects_callbacks_for_non_owned_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 != 0) + { + /* 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 = 0; + } + + 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; + const 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 != 0) + { + /* 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 **/ + +const 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 != 0) + return bucket->element; + else + /* If `bucket' is 0, 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, const void *element) +{ + size_t i; + + for (i = 0; i < array->slot_count; ++i) + { + objects_array_bucket_t *bucket = array->slots[i]; + + if (bucket != 0) + 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, const void *element) +{ + /* Note that this search is quite inefficient. */ + return objects_array_index_of_element (array, element) < (array->slot_count); +} + +const void ** +objects_array_all_elements (objects_array_t *array) +{ + objects_array_enumerator_t enumerator; + const void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (const void **)NSZoneCalloc(objects_array_zone(array), + count + 1, + sizeof(const 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; +} + +const void ** +objects_array_all_elements_ascending (objects_array_t *array) +{ + objects_array_enumerator_t enumerator; + const void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (const void **)NSZoneCalloc(objects_array_zone(array), + count + 1, + sizeof(const 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; +} + +const void ** +objects_array_all_elements_descending (objects_array_t *array) +{ + objects_array_enumerator_t enumerator; + const void **elements; + size_t count, i; + + count = objects_array_count (array); + + /* Set aside space to hold the elements. */ + elements = (const void **)NSZoneCalloc(objects_array_zone(array), + count + 1, + sizeof(const 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 != 0) + _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, + const 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, const void *element) +{ + if (objects_array_contains_element (array, element)) + objects_array_remove_element_known_present (array, element); + + return; +} + +/** Adding **/ + +const void * +objects_array_at_index_put_element (objects_array_t *array, + size_t index, + const 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, + const void **element) +{ + objects_array_bucket_t *bucket; + + bucket = _objects_array_enumerator_next_bucket (enumerator); + + if (bucket != 0) + { + if (element != 0) + *element = bucket->element; + if (index != 0) + *index = bucket->index; + return 1; + } + else + { + if (element != 0) + *element = objects_array_not_an_element_marker (enumerator->array); + if (index != 0) + *index = 0; + return 0; + } +} + +int +objects_array_enumerator_next_element (objects_array_enumerator_t * enumerator, + const void **element) +{ + return objects_array_enumerator_next_index_and_element (enumerator, + 0, + 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, + 0); +} + +/** Comparing **/ + +int +objects_array_is_equal_to_array (objects_array_t *array1, objects_array_t *array2) +{ + size_t a, b; + const 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, + const void *(*fcn) (const void *, const void *), + const 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; + const 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; +// const 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/o_cbs.m b/Source/o_cbs.m new file mode 100644 index 000000000..fb361cd00 --- /dev/null +++ b/Source/o_cbs.m @@ -0,0 +1,151 @@ +/* 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: Mon Mar 11 02:17:32 EST 1996 + * Serial: 96.03.11.08 + * + * 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 **********************************/ + +/* 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_non_owned_void_p_hash, + (objects_compare_func_t) objects_non_owned_void_p_compare, + (objects_is_equal_func_t) objects_non_owned_void_p_is_equal, + (objects_retain_func_t) objects_non_owned_void_p_retain, + (objects_release_func_t) objects_non_owned_void_p_release, + (objects_describe_func_t) objects_non_owned_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, + const 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, + const void *thing1, + const 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, + const void *thing1, + const 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); +} + +const void * +objects_retain(objects_callbacks_t callbacks, + const 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; +} + +NSString * +objects_describe(objects_callbacks_t callbacks, + const 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/o_cbs_char_p.m b/Source/o_cbs_char_p.m new file mode 100644 index 000000000..315c8f6bf --- /dev/null +++ b/Source/o_cbs_char_p.m @@ -0,0 +1,133 @@ +/* Callbacks for (NUL-terminated) arrays of `char'. + * Copyright (C) 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Sat Feb 10 22:04:38 EST 1996 + * Updated: Mon Mar 11 03:09:33 EST 1996 + * Serial: 96.03.11.06 + * + * 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 **********************************/ + +/* FIXME: Is this right?!? */ +#define _OBJECTS_NOT_A_CHAR_P_MARKER (const void *)(-1) + +const void *objects_not_a_char_p_marker = _OBJECTS_NOT_A_CHAR_P_MARKER; + +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, + _OBJECTS_NOT_A_CHAR_P_MARKER +}; + +/**** Function Implementations ***********************************************/ + +size_t +objects_char_p_hash(const char *cptr) +{ + register const char *s = cptr; + register size_t h = 0; + register size_t c = 0; + + while (*s != '\0') + h ^= *(s++) << (c++); + + return h; +} + +int +objects_char_p_compare(const char *cptr, const char *dptr) +{ + register const char *s = (char *) cptr; + register const 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(const char *cptr, const char *dptr) +{ + register const char *s = cptr; + register const char *t = 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; + } +} + +const void * +objects_char_p_retain(const char *cptr) +{ + return (const void *)cptr; +} + +void +objects_char_p_release(char *cptr) +{ + return; +} + +NSString * +objects_char_p_describe(const char *cptr) +{ + /* FIXME: Code this. */ + return nil; +} + diff --git a/Source/o_cbs_id.m b/Source/o_cbs_id.m new file mode 100644 index 000000000..f93bce290 --- /dev/null +++ b/Source/o_cbs_id.m @@ -0,0 +1,95 @@ +/* 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 +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/* FIXME: Is this right?!? */ +#define _OBJECTS_NOT_AN_ID_MARKER (const void *)(-1) + +const void *objects_not_an_id_marker = _OBJECTS_NOT_AN_ID_MARKER; + +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, + _OBJECTS_NOT_AN_ID_MARKER +}; + +/**** 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 itself uses, but I'd hoped + * that there was already a built-in call to do this sort of thing. */ + +size_t +objects_id_hash(id obj) +{ + return (size_t)[obj hash]; +} + +int +objects_id_compare(id obj, id jbo) +{ + return (int)[obj compare:jbo]; +} + +int +objects_id_is_equal(id obj, id jbo) +{ + return (int)[obj isEqual:jbo]; +} + +const void * +objects_id_retain(id obj) +{ + return (const void *)[obj retain]; +} + +void +objects_id_release(id obj) +{ + [obj release]; + return; +} + +NSString * +objects_id_describe(id obj) +{ + return [obj description]; +} + diff --git a/Source/o_cbs_int.m b/Source/o_cbs_int.m new file mode 100644 index 000000000..68a8e08de --- /dev/null +++ b/Source/o_cbs_int.m @@ -0,0 +1,92 @@ +/* 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: Mon Mar 11 00:23:10 EST 1996 + * Serial: 96.03.11.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 + +/**** Type, Constant, and Macro Definitions **********************************/ + +/* FIXME: This isn't right. Fix it. */ +#define _OBJECTS_NOT_AN_INT_MARKER (const void *)(-1) + +const void *objects_not_an_int_marker = _OBJECTS_NOT_AN_INT_MARKER; + +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, + _OBJECTS_NOT_AN_INT_MARKER +}; + +/**** Function Implementations ***********************************************/ + +/* FIXME: We (like OpenStep) make the big assumption here that + * 'sizeof(int) <= sizeof(void *)'....This is probably not a good thing, + * but what can I do? */ + +size_t +objects_int_hash(int i) +{ + return (size_t)i; +} + +int +objects_int_compare(int i, int j) +{ + return i - j; +} + +int +objects_int_is_equal(int i, int j) +{ + return i == j; +} + +const void * +objects_int_retain(int i) +{ + return (const void *)i; +} + +void +objects_int_release(int i) +{ + return; +} + +NSString * +objects_int_describe(int i) +{ + /* FIXME: Code this. */ + return nil; +} + + diff --git a/Source/o_cbs_int_p.m b/Source/o_cbs_int_p.m new file mode 100644 index 000000000..4bf038ac4 --- /dev/null +++ b/Source/o_cbs_int_p.m @@ -0,0 +1,94 @@ +/* Callbacks for pointers to `int'. + * 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 +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/* FIXME: Is this right?!? */ +#define _OBJECTS_NOT_AN_INT_P_MARKER (const void *)(-1) + +const void *objects_not_an_int_p_marker = _OBJECTS_NOT_AN_INT_P_MARKER; + +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, + _OBJECTS_NOT_AN_INT_P_MARKER +}; + +/**** Function Implementations ***********************************************/ + +size_t +objects_int_p_hash(const int *iptr) +{ + return (size_t)(iptr) / 4; +} + +int +objects_int_p_compare(const int *iptr, const int *jptr) +{ + if (iptr < jptr) + return -1; + else if (iptr > jptr) + return 1; + else /* (iptr == jptr) */ + return 0; +} + +int +objects_int_p_is_equal(const int *iptr, const int *jptr) +{ + /* FIXME: Is this right? If not, what else could it be? */ + return iptr == jptr; +} + +const void * +objects_int_p_retain(const int *iptr) +{ + return (const void *)iptr; +} + +void +objects_int_p_release(int *iptr) +{ + return; +} + +NSString * +objects_int_p_describe(const int *iptr) +{ + /* FIXME: Code this. */ + return nil; +} + + diff --git a/Source/o_cbs_void_p.m b/Source/o_cbs_void_p.m new file mode 100644 index 000000000..f17669077 --- /dev/null +++ b/Source/o_cbs_void_p.m @@ -0,0 +1,151 @@ +/* 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: Mon Mar 11 02:03:13 EST 1996 + * Serial: 96.03.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 +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/* FIXME: Is this right?!? */ +#define _OBJECTS_NOT_A_VOID_P_MARKER (const void *)(-1) + +const void *objects_not_a_void_p_marker = _OBJECTS_NOT_A_VOID_P_MARKER; + +objects_callbacks_t objects_callbacks_for_non_owned_void_p = +{ + (objects_hash_func_t) objects_non_owned_void_p_hash, + (objects_compare_func_t) objects_non_owned_void_p_compare, + (objects_is_equal_func_t) objects_non_owned_void_p_is_equal, + (objects_retain_func_t) objects_non_owned_void_p_retain, + (objects_release_func_t) objects_non_owned_void_p_release, + _OBJECTS_NOT_A_VOID_P_MARKER +}; + +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, + _OBJECTS_NOT_A_VOID_P_MARKER +}; + +/**** Function Implementations ***********************************************/ + +size_t +objects_non_owned_void_p_hash(register const void *cptr) +{ + return ((size_t) cptr)/4; +} + +int +objects_non_owned_void_p_compare(register const void *cptr, + register const void *dptr) +{ + if (cptr == dptr) + return 0; + else if (cptr < dptr) + return -1; + else /* if (cptr > dptr) */ + return 1; +} + +int +objects_non_owned_void_p_is_equal(register const void *cptr, + register const void *dptr) +{ + return (cptr == dptr); +} + +const void * +objects_non_owned_void_p_retain(const void *cptr) +{ + return cptr; +} + +void +objects_non_owned_void_p_release(void *cptr) +{ + /* We don't own CPTR, so we don't release it. */ + return; +} + +NSString * +objects_non_owned_void_p_describe(const void *cptr) +{ + /* FIXME: Code this. */ + return nil; +} + +size_t +objects_owned_void_p_hash(register const 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(register const void *cptr, + register const 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 const void *cptr, + register const void *dptr) +{ + return (cptr == dptr); +} + +const void * +objects_owned_void_p_retain(const void *cptr) +{ + return cptr; +} + +void +objects_owned_void_p_release(void *cptr) +{ + free((void *)cptr); + return; +} + +NSString * +objects_owned_void_p_describe(const void *obj) +{ + /* FIXME: Code this. */ + return nil; +} + diff --git a/Source/o_hash.m b/Source/o_hash.m new file mode 100644 index 000000000..7d8afcbc9 --- /dev/null +++ b/Source/o_hash.m @@ -0,0 +1,1130 @@ +/* A hash table. + * Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: ??? ??? ?? ??:??:?? ??? 1993 + * Updated: Tue Mar 19 00:25:18 EST 1996 + * Serial: 96.03.19.33 + * + * 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 ***********************************************/ + +/** Behind-the-Scenes functions **/ + +static inline objects_hash_bucket_t * +_objects_hash_pick_bucket_for_element(objects_hash_t *hash, + objects_hash_bucket_t *buckets, + size_t bucket_count, + const void *element) +{ + return buckets + (objects_hash(objects_hash_element_callbacks(hash), + element, hash) + % bucket_count); +} + +static inline 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); +} + +static inline objects_hash_bucket_t * +_objects_hash_bucket_for_element(objects_hash_t *hash, const void *element) +{ + return _objects_hash_pick_bucket_for_element(hash, hash->buckets, + hash->bucket_count, element); +} + +static inline 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); +} + +static inline void +_objects_hash_link_node_into_bucket(objects_hash_bucket_t *bucket, + objects_hash_node_t *node) +{ + if (bucket->first_node != 0) + bucket->first_node->prev_in_bucket = node; + + node->next_in_bucket = bucket->first_node; + + bucket->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_bucket->next_in_bucket = node->next_in_bucket; + if (node->next_in_bucket != 0) + node->next_in_bucket->prev_in_bucket = node->prev_in_bucket; + + node->prev_in_bucket = node->next_in_bucket = 0; + + return; +} + +static inline void +_objects_hash_link_node_into_hash(objects_hash_t *hash, + objects_hash_node_t *node) +{ + if (hash->first_node != 0) + hash->first_node->prev_in_hash = node; + node->next_in_hash = hash->first_node; + hash->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_hash->next_in_hash = node->next_in_hash; + if (node->next_in_hash != 0) + node->next_in_hash->prev_in_hash = node->prev_in_hash; + + node->prev_in_hash = node->next_in_hash = 0; + + return; +} + +static inline void +_objects_hash_add_node_to_bucket(objects_hash_bucket_t *bucket, + objects_hash_node_t *node) +{ + if (bucket != 0) + { + _objects_hash_link_node_into_bucket(bucket, node); + + node->bucket = bucket; + + bucket->node_count += 1; + bucket->element_count += 1; + } + + return; +} + +static inline 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; +} + +static inline void +_objects_hash_add_node_to_hash(objects_hash_t *hash, objects_hash_node_t *node) +{ + if (hash != 0) + { + _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; +} + +static inline void +_objects_hash_remove_node_from_its_bucket(objects_hash_node_t *node) +{ + if (node->bucket != 0) + { + node->bucket->node_count -= 1; + node->bucket->element_count -= 1; + + _objects_hash_unlink_node_from_its_bucket(node); + } + + return; +} + +static inline void +_objects_hash_remove_node_from_its_hash(objects_hash_node_t *node) +{ + if (node->hash != 0) + { + 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; +} + +static inline objects_hash_bucket_t * +_objects_hash_new_buckets(objects_hash_t *hash, size_t bucket_count) +{ + return (objects_hash_bucket_t *)NSZoneCalloc(objects_hash_zone(hash), + bucket_count, + sizeof(objects_hash_bucket_t)); +} + +static inline void +_objects_hash_free_buckets(objects_hash_t *hash, objects_hash_bucket_t *buckets) +{ + if (buckets != 0) + NSZoneFree(objects_hash_zone(hash), buckets); + return; +} + +static inline 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) != 0) + { + _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; +} + +static inline objects_hash_node_t * +_objects_hash_new_node(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + /* Allocate the space for a new node. */ + node = (objects_hash_node_t *)NSZoneMalloc(objects_hash_zone(hash), + sizeof(objects_hash_node_t)); + + if (node != 0) + { + /* Retain ELEMENT. (It's released in `_objects_hash_free_node()'.) */ + objects_retain(objects_hash_element_callbacks(hash), element, hash); + + /* Remember ELEMENT. */ + node->element = element; + + /* Associate NODE with HASH. */ + node->hash = hash; + + /* Zero out the various pointers. */ + node->bucket = 0; + node->next_in_bucket = 0; + node->next_in_hash = 0; + node->prev_in_bucket = 0; + node->prev_in_hash = 0; + } + + return node; +} + +static inline void +_objects_hash_free_node(objects_hash_node_t *node) +{ + if (node != 0) + { + 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), + (void *)node->element, + hash); + + /* Actually free the space hash aside for NODE. */ + NSZoneFree(objects_hash_zone(hash), node); + } + + /* And just return. */ + return; +} + +static inline objects_hash_node_t * +_objects_hash_node_for_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node = 0; + + if (element != objects_hash_not_an_element_marker(hash)) + { + objects_hash_bucket_t *bucket = 0; + + /* 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 != 0) && !objects_is_equal(objects_hash_element_callbacks(hash), + element, + node->element, + hash); + node = node->next_in_bucket); + } + + /* Note that if ELEMENT is bogus or if none of the nodes' + * elements matches ELEMENT, then we naturally return 0. */ + return node; +} + +static inline 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 != 0) + enumerator->node = enumerator->node->next_in_hash; + + /* Send back NODE. */ + return node; +} + +/** Callbacks... **/ + +/* Return a hash index for HASH. Needed for the callbacks below. */ +size_t +_objects_hash_hash(objects_hash_t *hash) +{ + /* One might be tempted to do something simple here, but remember: + * If two hash tables are equal they *must* hash to the same value! */ + + /* FIXME: Code this. */ + return 0; +} + +/* An (inefficient, but necessary) "retaining" function for hash tables. */ +objects_hash_t * +_objects_hash_retain(objects_hash_t *hash, objects_hash_t *in_hash) +{ + /* Note that this works only because all the structures (hash, map + * list, array) look alike at first...so we can get the zone of + * one just like we can get the zone of any of them. */ + return objects_hash_copy_with_zone(hash, objects_hash_zone(in_hash)); +} + +/* Returns a collection of callbacks for use with hash tables. */ +objects_callbacks_t +objects_callbacks_for_hash(void) +{ + objects_callbacks_t hash_callbacks = + { + (objects_hash_func_t) _objects_hash_hash, + (objects_compare_func_t) 0, + (objects_is_equal_func_t) objects_hash_is_equal_to_hash, + (objects_retain_func_t) _objects_hash_retain, + (objects_release_func_t) objects_hash_dealloc, + (objects_describe_func_t) objects_hash_description, + 0 + }; + + return hash_callbacks; +} + +/** 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 != 0) + { + _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, const void *element) +{ + objects_hash_node_t *node; + node = _objects_hash_node_for_element(hash, element); + + return node != 0; +} + +const void * +objects_hash_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + /* Try and find the node for ELEMENT. */ + node = _objects_hash_node_for_element(hash, element); + + if (node != 0) + return node->element; + else + return objects_hash_not_an_element_marker(hash); +} + +const void ** +objects_hash_all_elements(objects_hash_t *hash) +{ + size_t j; + const void **array; + objects_hash_enumerator_t enumerator; + + /* FIXME: It probably shouldn't be the programmer's responsibility to + * worry about freeing ARRAY. Maybe we should be returning an NSArray? */ + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_hash_zone(HASH), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_hash_zone(hash), + hash->node_count + 1, + sizeof(void *)); + + /* ENUMERATOR is an enumerator for HASH. */ + enumerator = objects_hash_enumerator_for_hash(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_for_hash()' 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_for_hash(hash); + * objects_hash_node_t *node; + * + * while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + * { + * _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_for_hash(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, + const 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 != 0) + { + /* 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-`0'. */ + if (element != 0) + *element = node->element; + + /* Since we weren't at the end of our enumeration, we return ``true''. */ + return 1; + } + else /* (node == 0) */ + { + /* If NODE isn't real, then we return the bogus element indicator and + * a zero count. */ + if (element != 0) + *element = objects_hash_not_an_element_marker(enumerator->hash); + + /* Since we're at the end of the enumeration, we return ``false''. */ + return 0; + } +} +/** Adding... **/ + +inline const void * +objects_hash_add_element_known_absent(objects_hash_t *hash, + const void *element) +{ + /* Note that we *do not* use the callback functions to test for + * the presence of the bogus element. Is is perfectly permissible for + * elements which are "equal" (but not equal) to the "not an + * element marker" to be added to HASH. */ + if (element == objects_hash_not_an_element_marker(hash)) + { + /* FIXME: We should do something useful here, + * like raise an exception. */ + abort(); + } + else if ((_objects_hash_node_for_element(hash, element)) != 0) + { + /* FIXME: We should do something useful here, + * like raise an exception. */ + abort(); + } + else /* (element != bogus && !(element in hash)) */ + { + objects_hash_node_t *node; + node = _objects_hash_new_node(hash, element); + + if (node != 0) + { + /* Actually add NODE to HASH. */ + _objects_hash_add_node_to_hash(hash, node); + + return node->element; + } + else /* (node == 0) */ + return objects_hash_not_an_element_marker(hash); + } +} + +const void * +objects_hash_add_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + + /* First, we check for ELEMENT in HASH. */ + node = _objects_hash_node_for_element(hash, element); + + if (node == 0) + { + /* ELEMENT isn't in HASH, so we can add it with impunity. */ + return objects_hash_add_element_known_absent(hash, element); + } + else /* (node != 0) */ + { + /* Remember: First retain, then release. */ + objects_retain(objects_hash_element_callbacks(hash), element, hash); + objects_release(objects_hash_element_callbacks(hash), + (void *)(node->element), + hash); + return node->element = element; + } +} + +/* If (any item "equal" to) ELEMENT is in HASH, then that member of HASH is + * returned. Otherwise, the "not an element marker" for HASH is returned + * and ELEMENT is added to HASH. */ +const void * +objects_hash_add_element_if_absent(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + + /* First, we check for ELEMENT in HASH. */ + node = _objects_hash_node_for_element(hash, element); + + if (node == 0) + { + /* ELEMENT isn't in HASH, so we can add it with impunity. */ + objects_hash_add_element_known_absent(hash, element); + + /* To indicate that ELEMENT was not in HASH, we return the bogus + * element indicator. */ + return objects_hash_not_an_element_marker(hash); + } + else /* (node != 0) */ + return node->element; +} +/** Removing **/ + +void +objects_hash_remove_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + node = _objects_hash_node_for_element(hash, element); + + if (node != 0) + { + /* 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_for_hash(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)) != 0) + { + _objects_hash_remove_node_from_its_hash(node); + _objects_hash_free_node(node); + } + + /* And return. */ + return; +} +/** Creating **/ + +objects_hash_t * +objects_hash_alloc_with_zone(NSZone * zone) +{ + objects_hash_t *hash; + /* Get a new hash, using basic methods. */ + hash = _objects_hash_alloc_with_zone(zone); + + return hash; +} + +objects_hash_t * +objects_hash_alloc(void) +{ + return objects_hash_alloc_with_zone(0); +} + +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_zone_with_callbacks(NSZone * zone, + objects_callbacks_t callbacks) +{ + return objects_hash_init_with_callbacks(objects_hash_alloc_with_zone(zone), + callbacks); +} + +objects_hash_t * +objects_hash_with_zone(NSZone * zone) +{ + return objects_hash_init(objects_hash_alloc_with_zone(zone)); +} + +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_non_owned_void_p(void) +{ + return objects_hash_with_callbacks(objects_callbacks_for_non_owned_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_int_p(void) +{ + return objects_hash_with_callbacks(objects_callbacks_for_int_p); +} + +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 != 0) + { + size_t capacity = 10; + /* Make a note of the callbacks 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 = 0; + hash->buckets = 0; + + /* 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 != 0) + { + objects_hash_enumerator_t enumerator; + const 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 = 0; + hash->buckets = 0; + + /* 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_for_hash(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... **/ + +/* If (some item "equal" to) ELEMENT is an element of HASH, then ELEMENT is + * substituted for it. (This is rather like the non-existant but perfectly + * reasonable 'objects_hash_add_element_if_present()'.) */ +void +objects_hash_replace_element(objects_hash_t *hash, const void *element) +{ + objects_hash_node_t *node; + + /* Lookup the node for ELEMENT. */ + node = _objects_hash_node_for_element(hash, element); + + if (node != 0) + { + /* 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), + (void *)(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; + const void *element; + enumerator = objects_hash_enumerator_for_hash(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; + const void *element; + /* Get an element enumerator for OTHER_HASH. */ + enumerator = objects_hash_enumerator_for_hash(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 in ZONE. */ +objects_hash_t * +objects_hash_copy_with_zone(objects_hash_t *old_hash, NSZone * zone) +{ + objects_hash_t *new_hash; + /* Alloc the NEW_HASH, copying over the low-level stuff. */ + new_hash = _objects_hash_copy_with_zone(old_hash, zone); + + /* 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 the default zone. */ +objects_hash_t * +objects_hash_copy(objects_hash_t *old_hash) +{ + return objects_hash_copy_with_zone(old_hash, 0); +} + +/** 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, + const void *(*fcn)(const void *, const void *), + const void *user_data) +{ + objects_hash_enumerator_t enumerator; + objects_hash_node_t *node; + enumerator = objects_hash_enumerator_for_hash(hash); + + while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + { + const 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), + (void *)(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_for_hash(hash); + + while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + 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_for_hash(hash); + + /* FIXME: Make this more efficient by enumerating + * over the smaller of the two hashes only. */ + while ((node = _objects_hash_enumerator_next_node(&enumerator)) != 0) + 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; + const void *element; + + enumerator = objects_hash_enumerator_for_hash(other_hash); + + while (objects_hash_enumerator_next_element(&enumerator, &element)) + objects_hash_add_element_if_absent(hash, element); + + return hash; +} + +/** Describing a hash table... **/ + +NSString * +objects_hash_description(objects_hash_t *hash) +{ +/* FIXME: Fix this. + NSMutableString *string; + NSString *gnirts; + objects_callbacks_t callbacks; + objects_hash_enumerator_t enumerator; + const void *element; + + callbacks = objects_hash_element_callbacks(hash); + enumerator = objects_hash_enumerator_for_hash(hash); + string = [_objects_hash_description(hash) mutableCopy]; + + [[string retain] autorelease]; + +#define DESCRIBE(E) objects_describe(callbacks, (E), hash) + + [string appendFormat:@"element_count = %d;\n", objects_hash_count(hash)]; + [string appendFormat:@"not_an_element_marker = %@;\n", + DESCRIBE(objects_hash_not_an_element_marker(hash))]; + [string appendString:@"elements = {\n"]; + + while (objects_hash_enumerator_next_element(&enumerator, &element)) + [string appendFormat:@"%@,\n", DESCRIBE(element)]; + + [string appendFormat:@"%@};\n", + DESCRIBE(objects_hash_not_an_element_marker(hash))]; + +#undef DESCRIBE + + gnirts = [[[string copy] retain] autorelease]; + + [string release]; + + return gnirts; +*/ + return nil; +} diff --git a/Source/o_list.m b/Source/o_list.m new file mode 100644 index 000000000..a018a4596 --- /dev/null +++ b/Source/o_list.m @@ -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: Wed Mar 20 20:48:39 EST 1996 + * Serial: 96.03.20.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 +#include +#include + +/**** Function Implementations ***********************************************/ + +/** Background functions **/ + +static 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 != 0) + node->next_in_list->prev_in_list = node->prev_in_list; + if (node->prev_in_list != 0) + node->prev_in_list->next_in_list = node->next_in_list; + + node->list->node_count -= 1; + node->list->element_count -= 1; + + return; +} + +static inline objects_list_node_t * +_objects_list_new_node (objects_list_t *list, const void *element) +{ + objects_list_node_t *node; + + node = NSZoneMalloc(objects_list_zone(list), sizeof(objects_list_node_t)); + + if (node != 0) + { + node->list = list; + node->next_in_list = 0; + node->prev_in_list = 0; + 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), + (void*)node->element, + node->list); + NSZoneFree(objects_list_zone(list), node); + return; +} + +static 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 != 0 && n != 0) + { + node = node->prev_in_list; + ++n; + } + } + else + /* (n >= 0) */ + { + node = list->first_node; + + while (node != 0 && n != 0) + { + node = node->next_in_list; + --n; + } + } + + return node; +} + +static inline objects_list_node_t * +_objects_list_nth_node_for_element (objects_list_t *list, + long int n, + const void *element) +{ + objects_list_node_t *node; + + if (n < 0) + { + node = list->last_node; + + ++n; + + while (node != 0 && 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 != 0 && 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; +} + +static 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 != 0) + { + 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, const void *element) +{ + objects_list_enumerator_t enumerator; + const 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; + + /* 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, + const 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 != 0) + { + /* If NODE is real, then return the element it contains. */ + if (element != 0) + *element = node->element; + + /* Indicate that the enumeration continues. */ + return 1; + } + else + { + /* If NODE isn't real, then we return the ``bogus'' indicator. */ + if (element != 0) + *element = objects_list_not_an_element_marker (enumerator->list); + + /* Indicate that the enumeration is over. */ + return 0; + } +} + +/** Searching **/ + +const void * +objects_list_element (objects_list_t *list, const void *element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, 0, element); + + if (node != 0) + return node->element; + else + return objects_list_not_an_element_marker (list); +} + +const 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 != 0) + return node->element; + else + return objects_list_not_an_element_marker (list); +} + +const void * +objects_list_first_element (objects_list_t *list) +{ + if (list->first_node != 0) + return list->first_node->element; + else + return objects_list_not_an_element_marker (list); +} + +const void * +objects_list_last_element (objects_list_t *list) +{ + if (list->last_node != 0) + return list->last_node->element; + else + return objects_list_not_an_element_marker (list); +} + +/** Obtaining elements **/ + +const void ** +objects_list_all_elements (objects_list_t *list) +{ + objects_list_enumerator_t enumerator; + const void **array; + size_t i; + + array = NSZoneCalloc(objects_list_zone(list), + objects_list_count(list) + 1, + sizeof(const void *)); + + for (i = 0; objects_list_enumerator_next_element (&enumerator, array + i); ++i); + + return array; +} + +/** Adding elements **/ + +const void * +objects_list_append_element (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element (list, -1, element); +} + +const void * +objects_list_append_element_if_absent (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element_if_absent (list, -1, element); +} + +const void * +objects_list_prepend_element (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element (list, 0, element); +} + +const void * +objects_list_prepend_element_if_absent (objects_list_t *list, const void *element) +{ + return objects_list_at_index_insert_element_if_absent (list, 0, element); +} + +const void * +objects_list_at_index_insert_element(objects_list_t *list, + long int n, + const 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 == 0) + /* FIXME: Make this a *little* more graceful, for goodness' sake! */ + abort(); + + if (n < 0) + { + if (node == 0) + { + anode = 0; + bnode = list->first_node; + } + else + /* (node != 0) */ + { + anode = node; + bnode = node->next_in_list; + } + } + else + /* (n >= 0) */ + { + if (node == 0) + { + anode = list->last_node; + bnode = 0; + } + else + /* (node != 0) */ + { + anode = node->prev_in_list; + bnode = node; + } + } + + new_node->prev_in_list = anode; + new_node->next_in_list = bnode; + + if (anode != 0) + anode->next_in_list = new_node; + if (bnode != 0) + 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; +} + +const void * +objects_list_at_index_insert_element_if_absent (objects_list_t *list, + long int n, + const 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, + const void *element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, n, element); + + if (node != 0) + { + _objects_list_remove_node_from_its_list (node); + _objects_list_free_node (list, node); + } + + return; +} + +void +objects_list_remove_element (objects_list_t *list, const void *element) +{ + objects_list_remove_nth_occurrance_of_element (list, 0, element); + return; +} + +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 != 0) + { + _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)) != 0) + { + _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, + const void *old_element, + const void *new_element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node_for_element (list, n, old_element); + + if (node != 0) + { + objects_retain (objects_list_element_callbacks (list), new_element, list); + objects_release (objects_list_element_callbacks (list), + (void*)node->element, + list); + node->element = new_element; + } + + return; +} + +void +objects_list_replace_element (objects_list_t *list, + const void *old_element, + const 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, + const void *new_element) +{ + objects_list_node_t *node; + + node = _objects_list_nth_node (list, n); + + if (node != 0) + { + objects_retain (objects_list_element_callbacks (list), new_element, list); + objects_release (objects_list_element_callbacks (list), + (void*)node->element, + list); + node->element = new_element; + } + + return; +} + +void +objects_list_replace_first_element (objects_list_t *list, + const void *new_element) +{ + objects_list_replace_nth_element (list, 0, new_element); + return; +} + +void +objects_list_replace_last_element (objects_list_t *list, + const void *new_element) +{ + objects_list_replace_nth_element (list, -1, new_element); + return; +} + +/** Creating **/ + +objects_list_t * +objects_list_alloc_with_zone (NSZone *zone) +{ + objects_list_t *list; + + list = _objects_list_alloc_with_zone(zone); + + return list; +} + +objects_list_t * +objects_list_alloc (void) +{ + return objects_list_alloc_with_zone (0); +} + +objects_list_t * +objects_list (void) +{ + return objects_list_init (objects_list_alloc ()); +} + +objects_list_t * +objects_list_with_zone (NSZone *zone) +{ + return objects_list_init (objects_list_alloc_with_zone(zone)); +} + +objects_list_t * +objects_list_with_zone_with_callbacks (NSZone *zone, + objects_callbacks_t callbacks) +{ + return objects_list_init_with_callbacks(objects_list_alloc_with_zone(zone), + 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_non_owned_void_p (void) +{ + return objects_list_with_callbacks (objects_callbacks_for_non_owned_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 != 0) + { + list->callbacks = callbacks; + list->element_count = 0; + list->node_count = 0; + list->first_node = 0; + list->last_node = 0; + } + + return list; +} + +objects_list_t * +objects_list_init_from_list (objects_list_t *list, objects_list_t *old_list) +{ + objects_list_enumerator_t enumerator; + const void *element; + + if (list != 0) + { + list->callbacks = objects_list_element_callbacks (old_list); + list->element_count = 0; + list->node_count = 0; + list->first_node = 0; + list->last_node = 0; + + if (old_list != 0) + { + /* 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; + const 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) +{ + return objects_list_copy_with_zone (old_list, 0); +} + +objects_list_t * +objects_list_copy_with_zone (objects_list_t *old_list, NSZone *zone) +{ + objects_list_t *list; + + /* Allocate a new (low-level) copy of OLD_LIST. */ + list = _objects_list_copy_with_zone(old_list, zone); + + /* Fill it in. */ + return objects_list_init_from_list (list, old_list); +} + +/** Mapping **/ + +objects_list_t * +objects_list_map_elements(objects_list_t *list, + const void *(*fcn)(const void *, void *), + void *user_data) +{ + objects_list_enumerator_t enumerator; + objects_list_node_t *node; + objects_callbacks_t callbacks; + + callbacks = objects_list_element_callbacks(list); + enumerator = objects_list_enumerator (list); + + while ((node = _objects_list_enumerator_next_node (&enumerator)) != 0) + { + const void *element; + + element = (*fcn)(node->element, user_data); + + objects_retain (callbacks, element, list); + objects_release (callbacks, (void *)(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 != 0) + { + objects_list_enumerator_t enumerator; + const 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 = 0; + hash->buckets = 0; + + /* 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 != 0) +// { +// objects_list_enumerator_t enumerator; +// const 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 = 0; +// chash->buckets = 0; +// +// /* 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/o_map.m b/Source/o_map.m new file mode 100644 index 000000000..18576513a --- /dev/null +++ b/Source/o_map.m @@ -0,0 +1,1379 @@ +/* A map table implementation. + * Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: ??? ??? ?? ??:??:?? ??? 1993 + * Updated: Tue Mar 12 02:12:37 EST 1996 + * Serial: 96.03.12.25 + * + * 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 **/ + +static inline objects_map_bucket_t * +_objects_map_pick_bucket_for_key(objects_map_t *map, + objects_map_bucket_t *buckets, + size_t bucket_count, + const void *key) +{ + return buckets + (objects_hash(objects_map_key_callbacks(map), + key, map) + % bucket_count); +} + +static inline 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); +} + +static inline objects_map_bucket_t * +_objects_map_bucket_for_key(objects_map_t *map, const void *key) +{ + return _objects_map_pick_bucket_for_key(map, map->buckets, + map->bucket_count, key); +} + +static inline 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); +} + +static inline void +_objects_map_link_node_into_bucket(objects_map_bucket_t *bucket, + objects_map_node_t *node) +{ + if (bucket->first_node != 0) + bucket->first_node->prev_in_bucket = node; + + node->next_in_bucket = bucket->first_node; + + bucket->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_bucket->next_in_bucket = node->next_in_bucket; + if (node->next_in_bucket != 0) + node->next_in_bucket->prev_in_bucket = node->prev_in_bucket; + + node->prev_in_bucket = node->next_in_bucket = 0; + + return; +} + +static inline void +_objects_map_link_node_into_map(objects_map_t *map, + objects_map_node_t *node) +{ + if (map->first_node != 0) + map->first_node->prev_in_map = node; + + node->next_in_map = map->first_node; + + map->first_node = node; + + return; +} + +static inline 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 != 0) + node->prev_in_map->next_in_map = node->next_in_map; + if (node->next_in_map != 0) + node->next_in_map->prev_in_map = node->prev_in_map; + + node->prev_in_map = node->next_in_map = 0; + + return; +} + +static inline void +_objects_map_add_node_to_bucket(objects_map_bucket_t *bucket, + objects_map_node_t *node) +{ + if (bucket != 0) + { + _objects_map_link_node_into_bucket(bucket, node); + + node->bucket = bucket; + + bucket->node_count += 1; + bucket->element_count += 1; + } + + return; +} + +static inline 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; +} + +static inline void +_objects_map_add_node_to_map(objects_map_t *map, objects_map_node_t *node) +{ + if (map != 0) + { + _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; +} + +static inline void +_objects_map_remove_node_from_its_bucket(objects_map_node_t *node) +{ + if (node->bucket != 0) + { + node->bucket->node_count -= 1; + node->bucket->element_count -= 1; + + _objects_map_unlink_node_from_its_bucket(node); + } + + return; +} + +static inline void +_objects_map_remove_node_from_its_map(objects_map_node_t *node) +{ + if (node->map != 0) + { + 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; +} + +static inline objects_map_bucket_t * +_objects_map_new_buckets(objects_map_t *map, size_t bucket_count) +{ + return (objects_map_bucket_t *)NSZoneCalloc(objects_map_zone(map), + bucket_count, + sizeof(objects_map_bucket_t)); +} + +static inline void +_objects_map_free_buckets(objects_map_t *map, objects_map_bucket_t *buckets) +{ + if (buckets != 0) + NSZoneFree(objects_map_zone(map), buckets); + return; +} + +static inline 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) != 0) + { + _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; +} + +static inline objects_map_node_t * +_objects_map_new_node(objects_map_t *map, const void *key, const void *value) +{ + objects_map_node_t *node; + /* Allocate the space for a new node. */ + node = (objects_map_node_t *)NSZoneMalloc(objects_map_zone(map), + sizeof(objects_map_node_t)); + + if (node != 0) + { + /* 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 = 0; + node->bucket = 0; + node->next_in_bucket = 0; + node->next_in_map = 0; + node->prev_in_bucket = 0; + node->prev_in_map = 0; + } + + return node; +} + +static inline void +_objects_map_free_node(objects_map_node_t *node) +{ + if (node != 0) + { + 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), + (void *)node->key, map); + objects_release(objects_map_value_callbacks(map), + (void *)node->value, map); + + /* Actually free the space map aside for NODE. */ + NSZoneFree(objects_map_zone(map), node); + } + + /* And just return. */ + return; +} + +static inline objects_map_node_t * +_objects_map_node_for_key(objects_map_t *map, const 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 != 0) && !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 `0'. */ + return node; +} + +/** Callbacks... **/ + +/* Return a hash index for MAP. Needed for the callbacks below. */ +size_t +_objects_map_hash(objects_map_t *map) +{ + /* One might be tempted to do something simple here, but remember: + * If two map tables are equal they *must* hash to the same value! */ + + /* FIXME: Code this. */ + return 0; +} + +/* An (inefficient, but necessary) "retaining" function for map tables. */ +objects_map_t * +_objects_map_retain(objects_map_t *map, objects_map_t *in_map) +{ + /* Note that this works only because all the structures (hash, map + * list, array) look alike at first...so we can get the zone of + * one just like we can get the zone of any of them. */ + return objects_map_copy_with_zone(map, objects_map_zone(in_map)); +} + +/* Returns a collection of callbacks for use with map tables. */ +objects_callbacks_t +objects_callbacks_for_map(void) +{ + objects_callbacks_t map_callbacks = + { + (objects_hash_func_t) _objects_map_hash, + (objects_compare_func_t) 0, + (objects_is_equal_func_t) objects_map_is_equal_to_map, + (objects_retain_func_t) _objects_map_retain, + (objects_release_func_t) objects_map_dealloc, + (objects_describe_func_t) objects_map_description, + 0 + }; + + return map_callbacks; +} + +/** 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 != 0) + { + _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... **/ + +/* Returns the number of key/value pairs in MAP. */ +size_t +objects_map_count(objects_map_t *map) +{ + return map->element_count; +} + +/* Returns some (inexact) measure of how many key/value pairs + * MAP can comfortably hold without resizing. */ +size_t +objects_map_capacity(objects_map_t *map) +{ + return map->bucket_count; +} + +/* Performs an internal consistency check, returns 'true' if + * everything is OK, 'false' otherwise. Really useful only + * for debugging. */ +int +objects_map_check(objects_map_t *map) +{ + /* FIXME: Code this. */ + return 1; +} + +int +objects_map_is_empty(objects_map_t *map) +{ + return objects_map_count(map) == 0; +} + +/** Searching **/ + +/* Returns 'true' if and only if some key in MAP is equal + * (in the sense of the key callbacks of MAP) to KEY. */ +int +objects_map_contains_key(objects_map_t *map, const void *key) +{ + if (_objects_map_node_for_key(map, key) != 0) + return 1; + else + return 0; +} + +/* Returns 'true' if and only if some value in MAP is equal + * (in the sense of the value callbacks of MAP) to VALUE. */ +/* WARNING: This is rather inefficient. Not to be used lightly. */ +int +objects_map_contains_value(objects_map_t *map, const void *value) +{ + objects_map_enumerator_t me; + const void *v = 0; + + /* ME is an enumerator for MAP. */ + me = objects_map_enumerator_for_map(map); + + /* Enumerate, and check. */ + while (objects_map_enumerator_next_value(&me, &v)) + if (objects_is_equal(objects_map_value_callbacks(map), value, v, map)) + return 1; + + /* If we made it this far, then VALUE isn't in MAP. */ + return 0; +} + +/* If KEY is in MAP, then the following three things happen: + * (1) 'true' is returned; + * (2) if OLD_KEY is non-zero, then the key in MAP + * equal to KEY is placed there; + * (3) if VALUE is non-zero, then the value in MAP + * mapped to by KEY is placed there. + * If KEY is not in MAP, then the following three things happen: + * (1) 'false' is returned; + * (2) if OLD_KEY is non-zero, then the "not a key marker" + * for MAP is placed there; + * (3) if VALUE is non-zero, then the the "not a value marker" + * for MAP is placed there. */ +inline int +objects_map_key_and_value_at_key(objects_map_t *map, + const void **old_key, + const void **value, + const void *key) +{ + objects_map_node_t *node; + + /* Try and find the node for KEY. */ + node = _objects_map_node_for_key(map, key); + + if (node != 0) + { + if (old_key != 0) + *old_key = node->key; + if (value != 0) + *value = node->value; + return 1; + } + else /* (node == 0) */ + { + if (old_key != 0) + *old_key = objects_map_not_a_key_marker(map); + if (value != 0) + *value = objects_map_not_a_value_marker(map); + return 0; + } +} + +/* If KEY is in MAP, then the key of MAP which is equal to KEY + * is returned. Otherwise, the "not a key marker" for MAP is returned. */ +const void * +objects_map_key_at_key(objects_map_t *map, const void *key) +{ + const void *old_key; + + /* Use the grandfather function above... */ + objects_map_key_and_value_at_key(map, &old_key, 0, key); + + return old_key; +} + +/* If KEY is in MAP, then the value of MAP which to which KEY maps + * is returned. Otherwise, the "not a value marker" for MAP is returned. */ +const void * +objects_map_value_at_key(objects_map_t *map, const void *key) +{ + const void *value; + + /* Use the grandfather function above... */ + objects_map_key_and_value_at_key(map, 0, &value, key); + + return value; +} + +const void ** +objects_map_all_keys_and_values(objects_map_t *map) +{ + size_t j; + const void **array; + objects_map_enumerator_t enumerator; + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_map_zone(MAP), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_map_zone(map), + 2 * (objects_map_count(map) + 1), + sizeof(void *)); + + /* ENUMERATOR is an enumerator for MAP. */ + enumerator = objects_map_enumerator_for_map(map); + + /* Now we enumerate through the elements of MAP, 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; +} + +const void ** +objects_map_all_keys(objects_map_t *map) +{ + size_t j; + const void **array; + objects_map_enumerator_t enumerator; + + /* FIXME: Morally, deallocating this space shouldn't be the + * programmer's responsibility. Maybe we should be returning + * an NSArray? */ + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_map_zone(MAP), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_map_zone(map), + objects_map_count(map) + 1, + sizeof(void *)); + + /* ENUMERATOR is an enumerator for MAP. */ + enumerator = objects_map_enumerator_for_map(map); + + /* Now we enumerate through the elements of MAP, 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; +} + +const void ** +objects_map_all_values(objects_map_t *map) +{ + size_t j; + const void **array; + objects_map_enumerator_t enumerator; + + /* FIXME: Morally, deallocating this space shouldn't be the + * programmer's responsibility. Maybe we should be returning + * an NSArray? */ + + /* Allocate space for ARRAY. Remember that it is the programmer's + * responsibility to free this by calling + * `NSZoneFree(objects_map_zone(MAP), ARRAY)' */ + array = (const void **)NSZoneCalloc(objects_map_zone(map), + objects_map_count(map) + 1, + sizeof(void *)); + + /* ENUMERATOR is an enumerator for MAP. */ + enumerator = objects_map_enumerator_for_map(map); + + /* Now we enumerate through the elements of MAP, 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 MAP WHILE YOU'RE ENUMERATING IT. */ + +/* IMPORTANT WARNING: Map 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_for_map()' 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_for_map(map); + * objects_map_node_t *node; + * + * while ((node = _objects_map_next_node(&enumerator)) != 0) + * { + * _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_for_map(objects_map_t *map) +{ + objects_map_enumerator_t enumerator; + /* Make sure ENUMERATOR knows its mapionary. */ + enumerator.map = map; + + /* Start ENUMERATOR at MAP'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 != 0) + 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, + const void **key, + const 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 != 0) + { + /* If NODE is real, then return the key and value it contains. */ + if (key != 0) + *key = node->key; + if (value != 0) + *value = node->value; + + /* Indicate that the enumeration continues. */ + return 1; + } + else + { + /* If NODE isn't real, then we return the ``bogus'' indicators. */ + if (key != 0) + *key = objects_map_not_a_key_marker(enumerator->map); + if (value != 0) + *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, + const void **key) +{ + return objects_map_enumerator_next_key_and_value(enumerator, key, 0); +} + +int +objects_map_enumerator_next_value(objects_map_enumerator_t *enumerator, + const void **value) +{ + return objects_map_enumerator_next_key_and_value(enumerator, 0, value); +} +/** Adding **/ + +/* FIXME: Make this check for invalidity and abort or raise an exception! */ + +const void * +objects_map_at_key_put_value_known_absent(objects_map_t *map, + const void *key, + const 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 != 0) + { + /* 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 `0' 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); + } +} + +const void * +objects_map_at_key_put_value(objects_map_t *map, + const void *key, + const void *value) +{ + objects_map_node_t *node; + /* First, we check for KEY in MAP. */ + node = _objects_map_node_for_key(map, key); + + if (node != 0) + { + objects_retain(objects_map_value_callbacks(map), value, map); + objects_release(objects_map_value_callbacks(map), + (void *)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); + } +} + +const void * +objects_map_at_key_put_value_if_absent(objects_map_t *map, + const void *key, + const 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 != 0) + { + /* 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, const 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 != 0) + { + /* 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_for_map(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)) != 0) + { + _objects_map_remove_node_from_its_map(node); + _objects_map_free_node(node); + } + + /* And return. */ + return; +} +/** Creating **/ + +objects_map_t * +objects_map_alloc_with_zone(NSZone * zone) +{ + objects_map_t *map; + map = _objects_map_alloc_with_zone(zone); + + return map; +} + +objects_map_t * +objects_map_alloc(void) +{ + return objects_map_alloc_with_zone(0); +} + +objects_map_t * +objects_map_with_zone(NSZone * zone) +{ + return objects_map_init(objects_map_alloc_with_zone(zone)); +} + +objects_map_t * +objects_map_with_zone_with_callbacks(NSZone * zone, + objects_callbacks_t key_callbacks, + objects_callbacks_t value_callbacks) +{ + return objects_map_init_with_callbacks(objects_map_alloc_with_zone(zone), + 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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_char_p, + objects_callbacks_for_non_owned_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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_non_owned_void_p, + objects_callbacks_for_non_owned_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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_int, + objects_callbacks_for_non_owned_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_non_owned_void_p(void) +{ + return objects_map_with_callbacks(objects_callbacks_for_id, + objects_callbacks_for_non_owned_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 != 0) + { + size_t capacity = 10; + + /* Make a note of the callbacks 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 = 0; + map->buckets = 0; + + /* 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; + const void *key; + const 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_for_map(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... **/ + +/* Releases all the keys and values of MAP, and then + * deallocates MAP itself. */ +void +objects_map_dealloc(objects_map_t *map) +{ + if (map != 0) + { + /* 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, + const void *key) +{ + objects_map_node_t *node; + + /* Look up the node (if any) for KEY in MAP. */ + node = _objects_map_node_for_key(map, key); + + if (node != 0) + { + /* Remember: First retain, then release; + * just in case they're the same. */ + objects_retain(objects_map_key_callbacks(map), key, map); + objects_release(objects_map_key_callbacks(map), + (void *)(node->key), map); + + /* Because "equality" is suppossedly transitive, we needn't + * worry about having duplicate keys in MAP after this. */ + node->key = key; + } + + return; +} +/** Comparing **/ + +/* Returns 'true' if every key/value pair of MAP2 is also a key/value pair + * of MAP1. Otherwise, returns 'false'. */ +int +objects_map_contains_map(objects_map_t *map1, objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key = 0; + const void *value = 0; + + /* ENUMERATOR is a key/value enumerator for MAP2. */ + enumerator = objects_map_enumerator_for_map(map2); + + while (objects_map_enumerator_next_key_and_value(&enumerator, &key, &value)) + { + objects_map_node_t *node; + + /* Try an get MAP1's node for KEY. */ + node = _objects_map_node_for_key(map1, key); + + /* If MAP1 doesn't even have KEY as a key, then we're done. */ + if (node == 0) + return 0; + + /* If MAP1 has K as a key, but doesn't map + * KEY to VALUE, then we're done. */ + if (objects_compare(objects_map_value_callbacks(map1), node->value, + value, map1)) + return 0; + } + + return 1; +} + +/* Returns 'true' iff some key/value pair of MAP1 if also + * a key/value pair of MAP2. */ +int +objects_map_intersects_map(objects_map_t *map1, objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key = 0; + const void *value = 0; + + /* ENUMERATOR is a key/value enumerator for MAP2. */ + enumerator = objects_map_enumerator_for_map(map2); + + while (objects_map_enumerator_next_key_and_value(&enumerator, &key, &value)) + { + objects_map_node_t *node; + + /* Try an get MAP1's node for KEY. */ + node = _objects_map_node_for_key(map1, key); + + /* If MAP1 doesn't even have KEY as a key, then we're done. */ + if (node != 0) + /* If MAP1 has KEY as a key, and maps KEY + * to VALUE, then we're done. Yippee! */ + if (objects_is_equal(objects_map_value_callbacks(map1), + node->value, value, map1)) + return 1; + } + + return 0; +} + +int +objects_map_is_equal_to_map(objects_map_t *map1, objects_map_t *map2) +{ + /* Check the counts. */ + if (objects_map_count(map1) != objects_map_count(map2)) + return 0; + + /* If the counts match, then we do an pair by pair 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; +} + +/* Returns 'true' iff every key of MAP2 is a key of MAP1. */ +int +objects_map_keys_contain_keys_of_map(objects_map_t *map1, objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key; + + enumerator = objects_map_enumerator_for_map(map2); + + if (objects_map_count(map1) < objects_map_count(map2)) + return 0; + + while (objects_map_enumerator_next_key(&enumerator, &key)) + if (!objects_map_contains_key(map1, key)) + return 0; + + return 1; +} + +/* Returns 'true' iff some key of MAP1 if also a key of MAP2. */ +int +objects_map_keys_intersect_keys_of_map(objects_map_t *map1, + objects_map_t *map2) +{ + objects_map_enumerator_t enumerator; + const void *key; + + enumerator = objects_map_enumerator_for_map(map1); + + while (objects_map_enumerator_next_key(&enumerator, &key)) + if (objects_map_contains_key(map2, key)) + return 1; + + return 0; +} + +/* Returns 'true' if MAP1 and MAP2 have the same number of key/value pairs, + * MAP1 contains every key of MAP2, and MAP2 contains every key of MAP1. + * Otherwise, returns 'false'. */ +int +objects_map_keys_are_equal_to_keys_of_map(objects_map_t *map1, + objects_map_t *map2); + +/** Copying **/ + +objects_map_t * +objects_map_copy_with_zone(objects_map_t *old_map, NSZone * zone) +{ + objects_map_t *new_map; + /* Alloc the NEW_MAP. */ + new_map = _objects_map_copy_with_zone(old_map, zone); + + /* 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) +{ + return objects_map_copy_with_zone(old_map, 0); +} + +/** Describing... **/ + +/* Returns a string describing (the contents of) MAP. */ +NSString * +objects_map_description(objects_map_t *map) +{ + /* FIXME: Code this. */ + return nil; +} + +/** Mapping **/ + +objects_map_t * +objects_map_map_keys(objects_map_t *map, + const void *(*fcn)(const void *, void *), + void *user_data) +{ + objects_map_enumerator_t enumerator; + objects_map_node_t *node; + + enumerator = objects_map_enumerator_for_map(map); + + while ((node = _objects_map_enumerator_next_node(&enumerator)) != 0) + { + const void *key; + + key = (*fcn)(node->key, user_data); + + objects_retain(objects_map_key_callbacks(map), key, map); + objects_release(objects_map_key_callbacks(map), + (void *)(node->key), map); + node->key = key; + } + + return map; +} + +objects_map_t * +objects_map_map_values(objects_map_t *map, + const void *(*fcn) (const void *, void *), + void *user_data) +{ + objects_map_enumerator_t enumerator; + objects_map_node_t *node; + + enumerator = objects_map_enumerator_for_map(map); + + while ((node = _objects_map_enumerator_next_node(&enumerator)) != 0) + { + const void *value; + + value = (*fcn)(node->value, user_data); + + objects_retain(objects_map_value_callbacks(map), value, map); + objects_release(objects_map_value_callbacks(map), + (void *)(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; + const void *key; + + enumerator = objects_map_enumerator_for_map(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; + const void *key; + + enumerator = objects_map_enumerator_for_map(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; + const void *key; + const void *value; + + enumerator = objects_map_enumerator_for_map(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/o_x_bas.m.in b/Source/o_x_bas.m.in new file mode 100644 index 000000000..c33eee591 --- /dev/null +++ b/Source/o_x_bas.m.in @@ -0,0 +1,261 @@ +/* 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: Mon Mar 11 00:56:35 EST 1996 + * Serial: 96.03.11.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 + +/**** Type, Constant, and Macro Definitions **********************************/ + +#define __@XX@__ 1 + +/**** Function Implementations ***********************************************/ + +/** Magic numbers... **/ + +/* Returns XX's magic number. */ +inline int +objects_@XX@_magic_number(objects_@XX@_t *xx) +{ + return xx->magic_number; +} + +/** Zones... **/ + +/* Returns the zone used to create and maintain XX. */ +inline NSZone * +objects_@XX@_zone(objects_@XX@_t *xx) +{ + return xx->zone; +} + +/** Names... **/ + +/* Returns the name that was given to XX. */ +inline NSString * +objects_@XX@_name(objects_@XX@_t *xx) +{ + return xx->name; +} + +/* Gives XX a name. */ +inline void +objects_@XX@_set_name(objects_@XX@_t *xx, NSString *name) +{ + [name retain]; + [xx->name release]; + xx->name = name; + return; +} + +/* Takes away XX's name. */ +inline void +objects_@XX@_unset_name(objects_@XX@_t *xx) +{ + [xx->name release]; + xx->name = nil; + return; +} + +/** Number **/ + +/* Returns the (process-wide) unique serial number given to the + * structure XX. See for more info. */ +inline size_t +objects_@XX@_serial_number(objects_@XX@_t *xx) +{ + return xx->serial_number; +} + +/* Gives XX a new (process-wide) unique number. Numbers are not + * reused. See for more info. */ +inline size_t +_objects_@XX@_set_serial_number(objects_@XX@_t *xx) +{ + size_t old_number; + + old_number = xx->serial_number; + xx->serial_number = (___objects_number_serialized)++; + + 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. */ +inline 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, (void *)(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''. */ +inline objects_callbacks_t +objects_@XX@_extra_callbacks(objects_@XX@_t *xx) +{ + return xx->extra_callbacks; +} + +/* Returns XX's ``extra'', a little extra space that each + * structure carries around with it. Its use is + * implementation-dependent. */ +inline const void * +objects_@XX@_extra(objects_@XX@_t *xx) +{ + return xx->extra; +} + +/* Sets XX's ``extra'', a little extra space that each structure + * carries around with it. Its use is implementation-dependent. */ +inline const void * +objects_@XX@_set_extra(objects_@XX@_t *xx, const void *extra) +{ + const 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, (void *)old_extra, xx); + + return old_extra; +} + +/* Resets XX's ``extra''. */ +inline void +objects_@XX@_unset_extra (objects_@XX@_t *xx) +{ + /* Release XX's extra. */ + objects_release(xx->extra_callbacks, (void *)(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 structures. */ +inline objects_@XX@_t * +_objects_@XX@_alloc_with_zone(NSZone *zone) +{ + objects_@XX@_t *xx; + + /* Try to allocate some space for XX. */ + xx = (objects_@XX@_t *) NSZoneMalloc(zone, sizeof(objects_@XX@_t)); + + /* The `objects_malloc()' was successful. */ + if (xx != 0) + { + _objects_@XX@_set_serial_number(xx); + xx->magic_number = _OBJECTS_MAGIC_@XX@; + xx->name = 0; + xx->zone = zone; + xx->extra_callbacks = objects_callbacks_for_non_owned_void_p; + xx->extra = 0; + + /* Increment the counter of allocated structures. */ + ++(___objects_number_allocated); + } + + return xx; +} + +/* Handles the universal, low-level deallocation of @XX@ structures. */ +inline void +_objects_@XX@_dealloc(objects_@XX@_t *xx) +{ + /* Make sure XX is valid. */ + if (xx != 0) + { + /* Free up any space we needed to keep track of XX's name. */ + if (xx->name != 0) + NSZoneFree(objects_@XX@_zone(xx), (char *)(xx->name)); + + /* Release XX's extra. */ + objects_@XX@_unset_extra(xx); + + /* Free up XX itself. */ + NSZoneFree(objects_@XX@_zone(xx), xx); + + /* Increment the counter of deallocated structures. */ + ++(___objects_number_deallocated); + } + + return; +} + +/* Handles the low-level copying of structures. */ +inline objects_@XX@_t * +_objects_@XX@_copy_with_zone(objects_@XX@_t *xx, NSZone *zone) +{ + objects_@XX@_t *new; + + /* Create a new structure. */ + new = _objects_@XX@_alloc_with_zone(zone); + + if (new != 0) + { + /* 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, objects_@XX@_extra_callbacks(xx)); + objects_@XX@_set_extra(new, objects_@XX@_extra(xx)); + } + + return new; +} + +inline NSString * +_objects_@XX@_description(objects_@XX@_t *xx) +{ + return [NSString stringWithFormat:@"magic_number = %#x;\nserial_number = %d;\nname = %@;\nextra = %@;\nzone = %@;\n", + objects_@XX@_magic_number(xx), + objects_@XX@_serial_number(xx), + objects_@XX@_name(xx), + objects_describe(objects_@XX@_extra_callbacks(xx), + objects_@XX@_extra(xx), xx), + NSZoneName(objects_@XX@_zone(xx))]; +} diff --git a/Source/o_x_cbs.m.in b/Source/o_x_cbs.m.in new file mode 100644 index 000000000..b90876841 --- /dev/null +++ b/Source/o_x_cbs.m.in @@ -0,0 +1,87 @@ +/* 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: Mon Mar 11 00:55:40 EST 1996 + * Serial: 96.03.11.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 + +/**** 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. */ +const 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. */ +const 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. */ +const void * +objects_@YY@_not_an_element_marker(objects_@YY@_t *yy) +{ + return (yy->callbacks).not_an_item_marker; +} + +#endif /* __map__ */ + diff --git a/Source/objects/numbers.h b/Source/objects/numbers.h new file mode 100644 index 000000000..5ad2dda8b --- /dev/null +++ b/Source/objects/numbers.h @@ -0,0 +1,85 @@ +/* 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:28:01 EST 1995 + * Updated: Mon Mar 18 14:36:49 EST 1996 + * Serial: 96.03.18.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. */ + +#ifndef __numbers_h_OBJECTS_INCLUDE +#define __numbers_h_OBJECTS_INCLUDE 1 + +/**** Included Headers *******************************************************/ + +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +/** Magic numbers... **/ + +/* Magic numbers for the different types of structures... */ +#define OBJECTS_MAGIC_ARRAY 0x1b658008 /* Thu Mar 2 02:28:50 EST 1994 */ +#define OBJECTS_MAGIC_DATA 0x1b651971 /* Fri Nov 24 21:46:14 EST 1995 */ +#define OBJECTS_MAGIC_HASH 0x1b653ee5 /* ??? ??? ?? ??:??:?? ??? 1993 */ +#define OBJECTS_MAGIC_HEAP 0x1b65beef /* Tue Sep 5 17:21:34 EDT 1995 */ +#define OBJECTS_MAGIC_LIST 0x1b65600d /* Tue Sep 5 17:23:50 EDT 1995 */ +#define OBJECTS_MAGIC_MAP 0x1b65abba /* ??? ??? ?? ??:??:?? ??? 1993 */ + +/* WARNING: Don't use these. They are not guaranteed to remain in future + * editions of this file. They are here only as a cheap fix for an + * annoying little problem. */ +/* FIXME: Get rid of these. See `x-basics.[ch].in' + * and `x-callbacks.[ch].in'. */ +#define _OBJECTS_MAGIC_array OBJECTS_MAGIC_ARRAY +#define _OBJECTS_MAGIC_data OBJECTS_MAGIC_DATA +#define _OBJECTS_MAGIC_hash OBJECTS_MAGIC_HASH +#define _OBJECTS_MAGIC_heap OBJECTS_MAGIC_HEAP +#define _OBJECTS_MAGIC_list OBJECTS_MAGIC_LIST +#define _OBJECTS_MAGIC_map OBJECTS_MAGIC_MAP + +/* Internal counters for the three functions below. They are placed here + * purely for your viewing pleasure. WARNING: Do not mess with these + * unless you know what you're doing. */ +extern size_t ___objects_number_allocated; +extern size_t ___objects_number_deallocated; +extern size_t ___objects_number_serialized; + +/**** Function Prototypes ****************************************************/ + +/* Returns the number of hash tables, map tables, lists, + * and sparse arrays allocated thus far. */ +size_t +_objects_number_allocated(void); + +/* Returns the number of hash tables, map tables, lists, + * and sparse arrays deallocated thus far. */ +size_t +_objects_number_deallocated(void); + +/* Returns (but does not increment) the number of hash tables, + * map tables, lists, and sparse arrays given serial numbers thus far. */ +size_t +_objects_number_serialized(void); + +/* Returns the least power of two strictly greater than BOUND. */ +size_t +_objects_next_power_of_two(size_t bound); + +#endif /* __numbers_h_OBJECTS_INCLUDE */ + diff --git a/Source/objects/x-bas.h.in b/Source/objects/x-bas.h.in new file mode 100644 index 000000000..52998211b --- /dev/null +++ b/Source/objects/x-bas.h.in @@ -0,0 +1,126 @@ +/* Basic functions for @XX@ structures. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Mon Dec 11 01:24:48 EST 1995 + * Updated: Mon Mar 11 00:54:50 EST 1996 + * Serial: 96.03.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. */ + +#ifndef __@XX@_bas_h_OBJECTS_INCLUDE +#define __@XX@_bas_h_OBJECTS_INCLUDE 1 + +/**** Included Headers *******************************************************/ + +#include +#include +#include +#include + +/**** Type, Constant, and Macro Definitions **********************************/ + +#define __@XX@__ 1 + +/**** Function Implementations ***********************************************/ + +/** Magic numbers... **/ + +/* Returns XX's magic number. */ +int +objects_@XX@_magic_number(objects_@XX@_t *xx); + +/** Zones... **/ + +/* Returns the allocs used to create and maintain XX. */ +NSZone * +objects_@XX@_zone(objects_@XX@_t *xx); + +/** Names... **/ + +/* Returns the name that was given to XX. */ +NSString * +objects_@XX@_name(objects_@XX@_t *xx); + +/* Gives XX a name. */ +void +objects_@XX@_set_name(objects_@XX@_t *xx, NSString *name); + +/* Takes away XX's name. */ +void +objects_@XX@_unset_name(objects_@XX@_t *xx); + +/** Serial numbers... **/ + +/* Returns the (process-wide) unique number given to the Libfn + * structure XX. See for more info. */ +size_t +objects_@XX@_serial_number(objects_@XX@_t *xx); + +/* 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); + +/** 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); + +/* Returns the callbacks associated with XX's ``extra''. */ +objects_callbacks_t +objects_@XX@_extra_callbacks(objects_@XX@_t *xx); + +/* Returns XX's ``extra'', a little extra space that each + * structure carries around with it. Its use is + * implementation-dependent. */ +const void * +objects_@XX@_extra(objects_@XX@_t *xx); + +/* Sets XX's ``extra'', a little extra space that each structure + * carries around with it. Its use is implementation-dependent. */ +const void * +objects_@XX@_set_extra(objects_@XX@_t *xx, const void *extra); + +/* Resets XX's ``extra''. */ +void +objects_@XX@_unset_extra(objects_@XX@_t *xx); + +/** Low-level Creation and Destruction **/ + +/* Handles the universal, low-level allocation of structures. */ +objects_@XX@_t * +_objects_@XX@_alloc_with_zone(NSZone *zone); + +/* Handles the universal, low-level deallocation of structures. */ +void +_objects_@XX@_dealloc(objects_@XX@_t *xx); + +/* Handles the low-level copying of structures. */ +objects_@XX@_t * +_objects_@XX@_copy_with_zone(objects_@XX@_t *xx, NSZone *zone); + +/* Returns a low-level description of XX. */ +NSString * +_objects_@XX@_description(objects_@XX@_t *xx); + +#endif /* __@XX@_bas_h_OBJECTS_INCLUDE */ + diff --git a/Source/objects/x-cbs.h.in b/Source/objects/x-cbs.h.in new file mode 100644 index 000000000..cf6c9f2af --- /dev/null +++ b/Source/objects/x-cbs.h.in @@ -0,0 +1,69 @@ +/* Getting callbacks from @YY@ structures. + * Copyright (C) 1995, 1996 Free Software Foundation, Inc. + * + * Author: Albin L. Jones + * Created: Mon Dec 11 03:41:00 EST 1995 + * Updated: Mon Mar 11 00:54:20 EST 1996 + * Serial: 96.03.11.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. */ + +#ifndef __@YY@_cbs_h_OBJECTS_INCLUDE +#define __@YY@_cbs_h_OBJECTS_INCLUDE 1 + +/**** 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); + +/* Returns the ``bogus'' marker associated with YY's keys. */ +const void *objects_@YY@_not_a_key_marker (objects_@YY@_t *yy); + +/* Returns the callbacks associated with YY's values. */ +objects_callbacks_t +objects_@YY@_value_callbacks (objects_@YY@_t *yy); + +/* Returns the ``bogus'' marker associated with YY's values. */ +const void *objects_@YY@_not_a_value_marker (objects_@YY@_t *yy); + +#else /* !__map__ */ + +/** Callbacks **/ + +/* Returns the callbacks associated with YY's elements. */ +objects_callbacks_t objects_@YY@_element_callbacks (objects_@YY@_t *yy); + +/* Returns the ``bogus'' marker associated with YY's elements. */ +const void *objects_@YY@_not_an_element_marker (objects_@YY@_t *yy); + +#endif /* __map__ */ + +#endif /* __@YY@_cbs_h_OBJECTS_INCLUDE */ +