From 505076a98d6068e436e87c341e3c3048f8bc8943 Mon Sep 17 00:00:00 2001 From: Jeff Teunissen Date: Sat, 11 Dec 2010 20:25:09 -0500 Subject: [PATCH] rewrite Array, AutoreleasePool This version is a bit more useful, and we should be able to get rid of the Stack class. Also, the Ruamoko parts of the autorelease system should be almost ready now. --- .gitignore | 1 + ruamoko/include/Array.h | 222 ++++++++++++++-- ruamoko/include/AutoreleasePool.h | 2 + ruamoko/lib/Array+Private.h | 32 +++ ruamoko/lib/Array+Private.r | 33 +++ ruamoko/lib/Array.r | 422 +++++++++++++++++++++++------- ruamoko/lib/AutoreleasePool.r | 62 ++--- ruamoko/lib/Makefile.am | 5 +- 8 files changed, 626 insertions(+), 153 deletions(-) create mode 100644 ruamoko/lib/Array+Private.h create mode 100644 ruamoko/lib/Array+Private.r diff --git a/.gitignore b/.gitignore index 811823d1d..d73b9f979 100644 --- a/.gitignore +++ b/.gitignore @@ -251,6 +251,7 @@ core /ruamoko/*.dat /ruamoko/*.qfo /ruamoko/*.sym +/ruamoko/doxygen/ # /ruamoko/cl_menu/ /ruamoko/cl_menu/*.dat diff --git a/ruamoko/include/Array.h b/ruamoko/include/Array.h index 743f2488c..3c4d80526 100644 --- a/ruamoko/include/Array.h +++ b/ruamoko/include/Array.h @@ -3,25 +3,213 @@ #include "Object.h" -@interface Array: Object +/** + A general ordered collection class + + The %Array class manages an ordered collection of objects. + If you want to subclass Array, you need to override these methods: + + \li #count() + \li #objectAtIndex: + \li #addObject: + \li #insertObject:atIndex: + \li #removeObjectAtIndex: + \li #removeLastObject + \li #replaceObjectAtIndex:withObject: +*/ +@interface Array: Object // { - integer count, size; - integer incr; - id [] array; + unsigned count; ///< The number of objects currently contained + unsigned capacity; ///< The number of objects that can be held right now + unsigned granularity; ///< The number of pointers allocated at a time (based on initial capacity) + id [] _objs; ///< A primitive array of pointers to objects } -- (id) init; -- (id) initWithIncrement: (integer) inc; -- (void) dealloc; -- (id) getItemAt: (integer) index; -- (void) setItemAt: (integer) index item:(id) item; -- (void) addItem: (id) item; -- (void) removeItem: (id) item; -- (id) removeItemAt: (integer) index; -- (id) insertItemAt: (integer) index item:(id) item; -- (integer) count; -- (integer) findItem: (id) item; --(void)makeObjectsPerformSelector:(SEL)selector; --(void)makeObjectsPerformSelector:(SEL)selector withObject:(id)arg; + +///\name Creating arrays +//\{ +/** + Create and return an empty array with the default initial capacity. +*/ ++ (id) array; + +/** + Creates and returns an empty array with initial capacity \a cap. + + \param cap The initial capacity of the array. +*/ ++ (id) arrayWithCapacity: (unsigned)cap; + +/** + Returns a copy of \a array, retaining its contents. +*/ ++ (id) arrayWithArray: (Array)array; + +/** + Create an array containing only \a anObject . +*/ ++ (id) arrayWithObject: (id)anObject; + +/** + Create an array from a list of objects. + + \warning Due to the nature of the Ruamoko/QuakeC language, do not supply + more than 6 objects to this method. +*/ ++ (id) arrayWithObjects: (id)firstObj, ...; + +/** + Create and return an array containing the first \a count objects from + primitive array \a objs. + + \warning Do not supply a primitive array containing fewer than \a count + objects. +*/ ++ (id) arrayWithObjects: (id [])objs + count: (unsigned)cnt; +//\} + +///\name Initializing arrays +//\{ +/** + Initialize the receiver with capacity \a cap +*/ +- (id) initWithCapacity: (unsigned)cap; + +/** + Initialize the receiver with objects from \a array. +*/ +- (id) initWithArray: (Array)array; + +/** + Initialize the receiver with objects from \a array. + + \param array The array to duplicate + \param flag if #YES, copies the contents instead of retaining them. +*/ +- (id) initWithArray: (Array)array + copyItems: (BOOL)flag; + +/** + Initialize the receiver with a list of objects. + + \warning Due to the nature of the Ruamoko/QuakeC language, do not supply + more than 6 objects to this method. +*/ +- (id) initWithObjects: (id)firstObj, ...; + +/** + Initialize the receiver with a primitive array of objects. +*/ +- (id) initWithObjects: (id [])objs + count: (unsigned)count; +//\} + +///\name Querying an array +//\{ +/** + Returns #YES if the receiver contains \a anObject. + + The #isEqual: method is used to determine this, so that (for example) a + newly-created string object can be compared to one already in the array. +*/ +- (BOOL) containsObject: (id)anObject; + +/** + Returns the number of objects contained in the receiver. +*/ +- (unsigned) count; + +/** + Returns the object contained at index \a index. +*/ +- (id) objectAtIndex: (unsigned)index; + +/** + Returns the contained object with the highest index. +*/ +- (id) lastObject; +//- (Enumerator) objectEnumerator; +//- (Enumerator) reverseObjectEnumerator; + +#if 0 +/** + Copies all object references in the receiver to \a aBuffer. + + \warning The destination buffer must be large enough to hold all contents. +*/ +- (BOOL) getObjects: (id [])aBuffer; +#endif +//\} + +///\name Adding objects +//\{ + +/** + Adds a single object to an array +*/ +- (void) addObject: (id)anObject; + +/** + Adds each object in \a array to the receiver, retaining it. +*/ +- (void) addObjectsFromArray: (Array)array; + +/** + Adds a single object to an array + + Adds \a anObject to the receiver at index \a index, pushing any objects + with a higher index to the next higher slot. +*/ +- (void) insertObject: (id)anObject + atIndex: (unsigned)index; +//\} + +///\name Replacing Objects +//\{ +- (void) replaceObjectAtIndex: (unsigned)index + withObject: (id)anObject; +- (void) setArray: (Array)array; +//\} + +///\name Removing Objects +//\{ + +/** + Recursively removes all objects from the receiver by sending + #removeLastObject to \a self, until #count() returns zero. +*/ +- (void) removeAllObjects; + + +- (void) removeLastObject; + +/** + Finds and removes all objects from the receiver that are equal to + \a anObject, by sending each #isEqual: with \a anObject as the argument. +*/ +- (void) removeObject: (id)anObject; + +/** + Finds and removes all objects from the receiver that are equal to any + objects in array \a anArray. +*/ +- (void) removeObjectsInArray: (Array)anArray; + +/** + Finds and removes all objects from the receiver that are \b exactly equal + to \a anObject, using a direct address comparison. +*/ +- (void) removeObjectIdenticalTo: (id)anObject; +- (void) removeObjectAtIndex: (unsigned)index; +//\} + +///\name Sending Messages to Elements +//\{ +- (void) makeObjectsPerformSelector: (SEL)selector; +- (void) makeObjectsPerformSelector: (SEL)selector + withObject: (id)arg; +//\} + @end #endif//__ruamoko_Array_h diff --git a/ruamoko/include/AutoreleasePool.h b/ruamoko/include/AutoreleasePool.h index 648c42d2a..1b59c1385 100644 --- a/ruamoko/include/AutoreleasePool.h +++ b/ruamoko/include/AutoreleasePool.h @@ -15,4 +15,6 @@ @end +void ARP_FreeAllPools (void); + #endif // __ruamoko_AutoreleasePool_h diff --git a/ruamoko/lib/Array+Private.h b/ruamoko/lib/Array+Private.h new file mode 100644 index 000000000..9ae37f946 --- /dev/null +++ b/ruamoko/lib/Array+Private.h @@ -0,0 +1,32 @@ +#ifndef __ruamoko_Array_Private_h +#define __ruamoko_Array_Private_h + +#include + +/** + Internal Array methods +*/ +@interface Array (Private) + +/** + Adds an object to the receiver, but without retaining it. + + This is a dangerous thing to do, and it's only done so that we can use an + Array to implement the AutoreleasePool class. + + \warning Using this method can result in crashes, and is only included + for internal use by other classes. +*/ +- (void) addObjectNoRetain: (id)anObject; + +/** + Removes an object from the receiver, but without releasing it. + + This can leak objects, and is only used so that we can use Arrays to + implement the autorelease system. +*/ +- (void) removeObjectNoRelease: (id)anObject; + +@end + +#endif //__ruamoko_Array_Private_h diff --git a/ruamoko/lib/Array+Private.r b/ruamoko/lib/Array+Private.r new file mode 100644 index 000000000..bc0000fff --- /dev/null +++ b/ruamoko/lib/Array+Private.r @@ -0,0 +1,33 @@ +#include "Array+Private.h" + +@implementation Array (Private) + +/** + This is a somewhat dangerous thing to do, and it's only done so that we can + use an Array to implement AutoreleasePool. +*/ +- (void) addObjectNoRetain: (id)anObject +{ + if (count == capacity) { + capacity += granularity; + _objs = (id [])obj_realloc (_objs, capacity * @sizeof (id)); + } + _objs[count++] = anObject; +} + +- (void) removeObjectNoRelease: (id)anObject +{ + local unsigned i = count; + local unsigned tmp; + + do { + if (_objs[--i] == anObject) { + for (tmp = i; tmp < count; tmp++) { + _objs[tmp] = _objs[tmp + 1]; + } + count--; + } + } while (i); +} + +@end diff --git a/ruamoko/lib/Array.r b/ruamoko/lib/Array.r index 948f7a874..6651160dc 100644 --- a/ruamoko/lib/Array.r +++ b/ruamoko/lib/Array.r @@ -1,143 +1,367 @@ +#include "math.h" + #include "Array.h" +#define STANDARD_CAPACITY 16 +#define ARRAY_MAX_GRANULARITY 100 + +/* + Optimization opportunity: + + if ([self class] == [Array class]) { + [[do things optimally instead of only through primitive methods]] + } +*/ + @implementation Array ++ (id) array +{ + return [self arrayWithCapacity: STANDARD_CAPACITY]; +} + ++ (id) arrayWithCapacity: (unsigned)cap +{ + return [[[self alloc] initWithCapacity: cap] autorelease]; +} + ++ (id) arrayWithArray: (Array)array +{ + return [[array copy] autorelease]; +} + ++ (id) arrayWithObject: (id)anObject +{ + Array newArray = (Array)[self arrayWithCapacity: STANDARD_CAPACITY]; + + [newArray addObject: anObject]; + return newArray; +} + ++ (id) arrayWithObjects: (id)firstObj, ... +{ + local integer i; + id newArray = [self arrayWithObject: firstObj]; + + for (i = 0; i < @args.count; i++) { + [newArray addObject: (id) @args.list[i].pointer_val]; + } + return [newArray autorelease]; +} + ++ (id) arrayWithObjects: (id []) objs count: (unsigned)cnt +{ + local integer i; + id newArray = [self array]; + + for (i = 0; i < cnt; i++) { + [newArray addObject: (id) objs[i]]; + } + return newArray; +} + - (id) init { - self = [super init]; - count = size = 0; - incr = 16; - array = NIL; - return self; + return [self initWithCapacity: STANDARD_CAPACITY]; } -- (id) initWithIncrement: (integer) inc +- (id) initWithCapacity: (unsigned)cap { + if (!(self = [super init])) + return NIL; + count = 0; - size = incr = inc; - array = (id []) obj_malloc (inc * @sizeof (id)); + if ((capacity = cap) < 1) + capacity = 1; + + granularity = (capacity + 1) / 2; + if (granularity < 1) + granularity = 1; + + if (granularity > ARRAY_MAX_GRANULARITY) + granularity = ARRAY_MAX_GRANULARITY; + + _objs = (id []) obj_malloc (capacity * @sizeof (id)); return self; } -- (void) dealloc +- (id) initWithArray: (Array)array +{ +#if 0 + local unsigned i; + local unsigned max = [array count]; + + if (!(self = [self initWithCapacity: max])) + return NIL; + + for (i = 0; i < max; i++) { + _objs[i] = [[array objectAtIndex: i] retain]; + } + return self; +#else + return [self initWithArray: array copyItems: NO]; +#endif +} + +- (id) initWithArray: (Array)array + copyItems: (BOOL)copy +{ + local unsigned i; + local unsigned max = [array count]; + + if (!(self = [self initWithCapacity: max])) + return NIL; + + for (i = 0; i < max; i++) { + if (copy) + _objs[i] = [[array objectAtIndex: i] copy]; + else + _objs[i] = [[array objectAtIndex: i] retain]; + } + return self; +} + +- (id) initWithObjects: (id)firstObj, ... { local integer i; - for (i = 0; i < count; i++) { - if (array[i]) - [array[i] release]; - } - if (array) { - obj_free (array); - } - [super dealloc]; -} -- (id) getItemAt: (integer) index -{ - if (index == -1) - index = count - 1; - if (index < 0 || index >= count) + if (!(self = [self initWithCapacity: @args.count + 1])) return NIL; - return array[index]; -} -- (void) setItemAt: (integer) index item: (id) item -{ - if (index == -1) - index = count - 1; - if (index < 0 || index >= count) - return; - [item retain]; - [array[index] release]; - array[index] = item; -} - -- (void) addItem: (id) item -{ - if (count == size) { - size += incr; - array = (id [])obj_realloc (array, size * @sizeof (id)); + [self addObject: firstObj]; + for (i = 0; i < @args.count; i++) { + [self addObject: (id) @args.list[i].pointer_val]; } - [item retain]; - array[count++] = item; + return self; } -- (void) removeItem: (id) item -{ - local integer i, n; - - for (i = 0; i < count; i++) - if (array[i] == item) { - count--; - for (n = i--; n < count; n++) - array[n] = array[n + 1]; - [item release]; - } - return; -} - -- (id) removeItemAt: (integer) index +- (id) initWithObjects: (id []) objs count: (unsigned)cnt { local integer i; - local id item; - if (index == -1) - index = count -1; - if (index < 0 || index >= count) + if (!(self = [self initWithCapacity: cnt])) return NIL; - item = array[index]; - count--; - for (i = index; i < count; i++) - array[i] = array[i + 1]; - [item release]; - return item; -} -- (id) insertItemAt: (integer) index item:(id) item -{ - local integer i; - if (index == -1) - index = count -1; - if (index < 0 || index >= count) - return NIL; - if (count == size) { - size += incr; - array = (id [])obj_realloc (array, size * @sizeof (id)); + for (i = 0; i < cnt; i++) { + [self addObject: (id) objs[i]]; } - for (i = count; i > index; i--) - array[i] = array[i - 1]; - [item retain]; - array[index] = item; - count++; - return item; + return self; } -- (integer) count +- (unsigned) count { return count; } -- (integer) findItem: (id) item +- (BOOL) containsObject: (id)anObject { - local integer i; - for (i = 0; i < count; i++) - if (array[i] == item) - return i; - return -1; + return [self indexOfObject: anObject] ? YES : NO; } --(void)makeObjectsPerformSelector:(SEL)selector +- (id) objectAtIndex: (unsigned)index { - local integer i; - for (i = 0; i < count; i++) - [array[i] performSelector:selector]; + if (index >= count) { + return NIL; // FIXME: need exceptions + } + return _objs[index]; } --(void)makeObjectsPerformSelector:(SEL)selector withObject:(id)arg +- (id) lastObject +{ + return [self objectAtIndex: [self count] - 1]; +} + +/* + Adding objects +*/ + +- (void) addObject: (id)anObject +{ + if (count == capacity) { + capacity += granularity; + _objs = (id [])obj_realloc (_objs, capacity * @sizeof (id)); + } + _objs[count++] = [anObject retain]; +} + +- (void) addObjectsFromArray: (Array)array +{ + local unsigned i; + + if (!array || array == self) + return; // FIXME: need exceptions + + for (i = 0; i < [array count]; i++) { + [self addObject: [array objectAtIndex: i]]; + } +} + +- (void) insertObject: (id)anObject + atIndex: (unsigned)index { local integer i; - for (i = 0; i < count; i++) - [array[i] performSelector:selector withObject:arg]; + + if (index >= count) + return; // FIXME: need exceptions + + if (count == capacity) { // at capacity, expand + _objs = (id [])obj_realloc (_objs, capacity * @sizeof (id)); + capacity += granularity; + } + + for (i = count; i > index; i--) { + _objs[i] = _objs[i - 1]; + } + + _objs[index] = [anObject retain]; + count++; + + return; +} + +/* + Replacing objects +*/ + +- (void) replaceObjectAtIndex: (unsigned)index + withObject: (id)anObject +{ + local id tmp; + + if (!anObject || index >= count) { + return; // FIXME: need exceptions + } + + // retain before release + tmp = _objs[index]; + _objs[index] = [anObject retain]; + [tmp release]; +} + +- (void) setArray: (Array)array +{ + if (self == array) + return; + + [self removeAllObjects]; + [self addObjectsFromArray: array]; +} + +/* + Object removal +*/ +- (void) removeAllObjects +{ +#if 0 + local id tmp; + + while (count) { + /* + We do it this way to avoid having something weird happen when + the object is released (dealloc may trigger, which in turn could + cause something else to happen). + */ + tmp = _objs[--count]; + _objs[i] = NIL; + [tmp release]; + } +#else + while ([self count]) { + [self removeLastObject]; + } +#endif +} + +- (void) removeLastObject +{ + local id tmp; + + tmp = _objs[--count]; + _objs[count] = NIL; + [tmp release]; +} + +- (void) removeObject: (id)anObject +{ + local unsigned i = [self count]; + do { + --i; + if ([[self objectAtIndex: i] isEqual: anObject]) { + [self removeObjectAtIndex: i]; + } + } while (i); +} + +- (void) removeObjectAtIndex: (unsigned)index +{ + local integer i; + local id temp; + + if (index >= count) + return; // FIXME: need exceptions + + temp = _objs[index]; + count--; + for (i = index; i < count; i++) { // reassign all objs >= index + _objs[i] = _objs[i+1]; + } + + [temp release]; +} + +- (void) removeObjectIdenticalTo: (id)anObject +{ + local unsigned i = [self count]; + do { + --i; + if ([self objectAtIndex: i] == anObject) { + [self removeObjectAtIndex: i]; + } + } while (i); +} + +- (void) removeObjectsInArray: (Array)array +{ + local unsigned i = [array count]; + + do { + --i; + [self removeObject: [array objectAtIndex: i]]; + } while (i); +} + +- (void) makeObjectsPerformSelector: (SEL)selector +{ + local integer i; + + for (i = 0; i < [self count]; i++) { + [[self objectAtIndex: i] performSelector: selector]; + } +} + +- (void) makeObjectsPerformSelector: (SEL)selector + withObject: (id)anObject +{ + local integer i; + + for (i = 0; i < [self count]; i++) { + [[self objectAtIndex: i] performSelector: selector withObject: anObject]; + } +} + +- (void) dealloc +{ + local unsigned i; + for (i = 0; i < count; i++) { + if (_objs[i]) + [_objs[i] release]; + } + + if (_objs) { + obj_free (_objs); + } + + [super dealloc]; } @end diff --git a/ruamoko/lib/AutoreleasePool.r b/ruamoko/lib/AutoreleasePool.r index 184732f88..def6a5e2a 100644 --- a/ruamoko/lib/AutoreleasePool.r +++ b/ruamoko/lib/AutoreleasePool.r @@ -1,12 +1,10 @@ #include "AutoreleasePool.h" //#include "Stack.h" -@static AutoreleasePool sharedInstance; -//@static Stack poolStack; +//@static AutoreleasePool sharedInstance; +@static Array poolStack; -@interface AutoreleasePool (Private) -- (void) addItem: (id)anItem; -@end +//@static Stack poolStack; @implementation AutoreleasePool @@ -15,28 +13,26 @@ if (!(self = [super init])) return NIL; - //if (!poolStack) - // poolStack = [Stack new]; + if (!poolStack) + poolStack = [[Array alloc] initWithCapacity: 1]; - if (!sharedInstance) - sharedInstance = self; + [poolStack addObjectNoRetain: self]; - array = [[Array alloc] init]; + array = [Array new]; return self; } + (void) addObject: (id)anObject { - if (!sharedInstance) + if (!poolStack || [poolStack count]) [[AutoreleasePool alloc] init]; - [sharedInstance addObject: anObject]; + + [[poolStack lastObject] addObject: anObject]; } - (void) addObject: (id)anObject { - [array addItem: anObject]; - [anObject release]; // the array retains the item, and releases when - // dealloced + [array addObjectNoRetain: anObject]; } - (id) retain @@ -44,33 +40,27 @@ [self error: "Don't send -retain to an autorelease pool."]; } -+ (void) release +- (id) autorelease { - [sharedInstance release]; - sharedInstance = NIL; -} - -- (/*oneway*/ void) release -{ - [self dealloc]; + [self error: "Don't send -autorelease to an autorelease pool."]; } - (void) dealloc { - //local id tmp; - [array release]; - - /* - This may be wrong. - Releasing an autorelease pool should keep popping pools off the stack - until it gets to itself. - */ - //do { - // tmp = [poolStack pop]; - //} while (tmp != self); - - sharedInstance = NIL; + [poolStack removeObjectNoRelease: self]; [super dealloc]; } + +- (/*oneway*/ void) release +{ + +} + @end + +void +ARP_FreeAllPools (void) +{ + [poolStack removeAllObjects]; +} diff --git a/ruamoko/lib/Makefile.am b/ruamoko/lib/Makefile.am index d02474cd8..e01b692b8 100644 --- a/ruamoko/lib/Makefile.am +++ b/ruamoko/lib/Makefile.am @@ -10,6 +10,9 @@ RANLIB=touch INCLUDES= -I$(top_srcdir)/ruamoko/include -I$(top_srcdir)/include +noinst_HEADERS= \ + Array+Private.h + ruamoko_libs=libr.a libqw.a libnq.a libcsqc.a if BUILD_RUAMOKO libs=$(ruamoko_libs) @@ -29,7 +32,7 @@ EXTRA_LIBRARIES= $(ruamoko_libs) libr_a_SOURCES=\ cbuf.r cmd.r cvar.r file.r hash.r plist.r qfile.r qfs.r script.r sound.r \ string.r \ - AutoreleasePool.r Array.r Entity.r List.r ListNode.r Object.r \ + AutoreleasePool.r Array.r Array+Private.r Entity.r List.r ListNode.r Object.r \ PropertyList.r Protocol.r Stack.r libr_a_AR=$(PAK) -cf