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.
This commit is contained in:
Jeff Teunissen 2010-12-11 20:25:09 -05:00
parent 4bd37e7b64
commit 505076a98d
8 changed files with 626 additions and 153 deletions

1
.gitignore vendored
View file

@ -251,6 +251,7 @@ core
/ruamoko/*.dat
/ruamoko/*.qfo
/ruamoko/*.sym
/ruamoko/doxygen/
# /ruamoko/cl_menu/
/ruamoko/cl_menu/*.dat

View file

@ -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 //<Copying>
{
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

View file

@ -15,4 +15,6 @@
@end
void ARP_FreeAllPools (void);
#endif // __ruamoko_AutoreleasePool_h

View file

@ -0,0 +1,32 @@
#ifndef __ruamoko_Array_Private_h
#define __ruamoko_Array_Private_h
#include <Array.h>
/**
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

View file

@ -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

View file

@ -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

View file

@ -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];
}

View file

@ -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