mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-02-16 00:21:29 +00:00
* GSSkipMutableArray.[hm]: New NSMutableArray subclass.
* GSIndexedSkipList.[hm]: Underlying C implementation. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@23633 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
dc6f3dda03
commit
05cebfc22b
6 changed files with 775 additions and 1 deletions
|
@ -1,3 +1,8 @@
|
|||
2006-09-27 Matt Rice <ratmice@yahoo.com>
|
||||
|
||||
* GSSkipMutableArray.[hm]: New NSMutableArray subclass.
|
||||
* GSIndexedSkipList.[hm]: Underlying C implementation.
|
||||
|
||||
2006-06-07 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* GSCache.h:
|
||||
|
|
|
@ -18,18 +18,23 @@ Performance_OBJC_FILES += \
|
|||
GSCache.m \
|
||||
GSThroughput.m \
|
||||
GSTicker.m \
|
||||
GSIndexedSkipList.m \
|
||||
GSSkipMutableArray.m \
|
||||
|
||||
|
||||
Performance_HEADER_FILES += \
|
||||
GSCache.h \
|
||||
GSThroughput.h \
|
||||
GSTicker.h \
|
||||
GSIndexedSkipList.h \
|
||||
GSSkipMutableArray.h \
|
||||
|
||||
|
||||
Performance_AGSDOC_FILES += \
|
||||
GSCache.h \
|
||||
GSThroughput.h \
|
||||
GSTicker.h
|
||||
GSTicker.h \
|
||||
GSSkipMutableArray.h
|
||||
|
||||
|
||||
# Optional Java wrappers for the library
|
||||
|
|
82
GSIndexedSkipList.h
Normal file
82
GSIndexedSkipList.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Matt Rice <ratmice@yahoo.com>
|
||||
Date: 2006
|
||||
|
||||
This file is part of the Performance 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
*/
|
||||
|
||||
#include <Foundation/NSZone.h>
|
||||
#define GSISLMaxNumberOfLevels 16
|
||||
#define GSISLMaxLevel 15
|
||||
|
||||
/*
|
||||
* attempt at caching the previously looked up index
|
||||
* to reduce the search time required when wantedIndex > previousIndex
|
||||
* this didn't seem to provide any benefit, actually negatively impacting
|
||||
* performance though it was never thoroughly tested it
|
||||
*/
|
||||
#define GSISL_CACHE_OPT 0
|
||||
|
||||
|
||||
|
||||
typedef id GSISLValueType;
|
||||
typedef struct GSISLNode_t *GSISLNode;
|
||||
extern GSISLNode GSISLNil;
|
||||
|
||||
struct GSISLForward_t
|
||||
{
|
||||
unsigned delta;
|
||||
GSISLNode next;
|
||||
};
|
||||
|
||||
struct GSISLNode_t
|
||||
{
|
||||
GSISLValueType value;
|
||||
struct GSISLForward_t forward[1];
|
||||
};
|
||||
|
||||
typedef struct GSIndexedSkipList
|
||||
{
|
||||
int level; /* Maximum level of the list
|
||||
(1 more than the number of levels in the list) */
|
||||
GSISLNode header; /* pointer to header */
|
||||
unsigned count;
|
||||
NSZone *zone;
|
||||
#if GSISL_CACHE_OPT
|
||||
unsigned indexCache[GSISLMaxNumberOfLevels];
|
||||
GSISLNode nodeCache[GSISLMaxNumberOfLevels];
|
||||
#endif
|
||||
} * GSISList;
|
||||
|
||||
void GSISLInitialize();
|
||||
void GSISLFreeList(GSISList l);
|
||||
GSISList GSISLInitList(NSZone *zone);
|
||||
void GSISLInsertItemAtIndex(GSISList l,
|
||||
GSISLValueType value,
|
||||
unsigned index);
|
||||
|
||||
GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index);
|
||||
|
||||
GSISLValueType GSISLRemoveItemAtIndex(GSISList l,
|
||||
unsigned index);
|
||||
|
||||
GSISLValueType GSISLReplaceItemAtIndex(GSISList l,
|
||||
GSISLValueType newVal,
|
||||
unsigned index);
|
||||
|
362
GSIndexedSkipList.m
Normal file
362
GSIndexedSkipList.m
Normal file
|
@ -0,0 +1,362 @@
|
|||
/**
|
||||
Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Matt Rice <ratmice@yahoo.com>
|
||||
Date: 2006
|
||||
|
||||
This file is part of the Performance 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "GSIndexedSkipList.h"
|
||||
|
||||
#define PrettyErr(x) do { fprintf(stderr, "%s:%i: %s\n",__FILE__, __LINE__, x); exit(EXIT_FAILURE); } while (0)
|
||||
|
||||
GSISLNode GSISLNil;
|
||||
|
||||
GSISLNode GSISLNewNodeOfLevel(int l, NSZone *zone)
|
||||
{
|
||||
GSISLNode ret = (GSISLNode) NSZoneMalloc(zone, sizeof(struct GSISLNode_t)
|
||||
+ ((l) * sizeof(struct GSISLForward_t)));
|
||||
|
||||
if (ret == NULL)
|
||||
{
|
||||
PrettyErr(strerror(errno));
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ret->forward[l].delta = 0;
|
||||
} while (--l >= 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GSISLInitialize()
|
||||
{
|
||||
if (GSISLNil != NULL) return;
|
||||
|
||||
GSISLNil = GSISLNewNodeOfLevel(0, NSDefaultMallocZone());
|
||||
GSISLNil->forward[0].delta = UINT_MAX;
|
||||
GSISLNil->value = nil;
|
||||
GSISLNil->forward[0].next = NULL;
|
||||
}
|
||||
|
||||
GSISList GSISLInitList(NSZone *zone)
|
||||
{
|
||||
GSISList l;
|
||||
int i;
|
||||
|
||||
l = (GSISList)NSZoneMalloc(zone, sizeof(struct GSIndexedSkipList));
|
||||
if (l == NULL)
|
||||
{
|
||||
PrettyErr(strerror(errno));
|
||||
}
|
||||
l->zone = zone;
|
||||
l->level = 0;
|
||||
l->count = 0;
|
||||
l->header = GSISLNewNodeOfLevel(GSISLMaxNumberOfLevels, l->zone);
|
||||
l->header->value = nil;
|
||||
|
||||
for (i=0; i < GSISLMaxNumberOfLevels; i++)
|
||||
{
|
||||
l->header->forward[0].delta = 0;
|
||||
l->header->forward[0].next = GSISLNil;
|
||||
#if GSISL_CACHE_OPT
|
||||
l->indexCache[i] = 0;
|
||||
l->nodeCache[i] = l->header;
|
||||
#endif
|
||||
}
|
||||
return(l);
|
||||
}
|
||||
|
||||
void GSISLFreeList(GSISList l)
|
||||
{
|
||||
GSISLNode p,q;
|
||||
|
||||
p = l->header;
|
||||
do
|
||||
{
|
||||
q = p->forward[0].next;
|
||||
NSZoneFree(l->zone,p);
|
||||
p = q;
|
||||
} while (p != GSISLNil);
|
||||
|
||||
NSZoneFree(l->zone, l);
|
||||
};
|
||||
|
||||
int GSISLRandomLevel()
|
||||
{
|
||||
int level = 0;
|
||||
static int p = RAND_MAX / 4;
|
||||
while (rand() < p && level < GSISLMaxLevel)
|
||||
{
|
||||
level++;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
void GSISLInsertItemAtIndex(GSISList l, GSISLValueType value,
|
||||
unsigned index)
|
||||
{
|
||||
int k, i;
|
||||
GSISLNode update[GSISLMaxNumberOfLevels];
|
||||
unsigned updateIndexes[GSISLMaxNumberOfLevels];
|
||||
GSISLNode p,q;
|
||||
unsigned depth;
|
||||
depth = 0;
|
||||
k = l->level;
|
||||
#if GSISL_CACHE_OPT
|
||||
|
||||
if (l->indexCache[k] < index)
|
||||
{
|
||||
p = l->nodeCache[k];
|
||||
depth = l->indexCache[k];
|
||||
}
|
||||
else
|
||||
{
|
||||
p = l->header;
|
||||
}
|
||||
#else
|
||||
p = l->header;
|
||||
#endif
|
||||
do
|
||||
{
|
||||
while (q = p->forward[k].next,
|
||||
q != GSISLNil && depth + p->forward[k].delta < index + 1)
|
||||
{
|
||||
depth += p->forward[k].delta;
|
||||
p = q;
|
||||
}
|
||||
updateIndexes[k] = depth;
|
||||
update[k] = p;
|
||||
} while(--k >= 0);
|
||||
|
||||
k = GSISLRandomLevel();
|
||||
q = GSISLNewNodeOfLevel(k, l->zone);
|
||||
|
||||
if (k > l->level)
|
||||
{
|
||||
/* we are creating a new level that looks like
|
||||
* header ---> new node ---> tail
|
||||
*/
|
||||
k = l->level;
|
||||
l->level++;
|
||||
#if GSISL_CACHE_OPT
|
||||
l->nodeCache[l->level] = l->header;
|
||||
l->indexCache[l->level] = 0;
|
||||
#endif
|
||||
l->header->forward[l->level].delta = index + 1;
|
||||
l->header->forward[l->level].next = q;
|
||||
q->forward[l->level].delta = 0;
|
||||
q->forward[l->level].next = GSISLNil;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if there are higher nodes than this nodes level.
|
||||
increment the deltas in the update, as we are inserting
|
||||
a node inbetween their starting point and their ending
|
||||
*/
|
||||
for (i = k + 1; i <= l->level; i++)
|
||||
{
|
||||
if (update[i]->forward[i].delta != 0)
|
||||
update[i]->forward[i].delta++;
|
||||
#if GSISL_CACHE_OPT
|
||||
l->nodeCache[i] = update[i];
|
||||
l->indexCache[i] = updateIndexes[i];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
q->value = value;
|
||||
do
|
||||
{
|
||||
/* update from the nodes highest level down to level 0
|
||||
* on all the levels already existing in the list
|
||||
*/
|
||||
p = update[k];
|
||||
|
||||
if (p->forward[k].delta)
|
||||
q->forward[k].delta = updateIndexes[k] + p->forward[k].delta - depth;
|
||||
|
||||
p->forward[k].delta = depth + 1 - updateIndexes[k];
|
||||
q->forward[k].next = p->forward[k].next;
|
||||
p->forward[k].next = q;
|
||||
#if GSISL_CACHE_OPT
|
||||
l->indexCache[k] = updateIndexes[k];
|
||||
l->nodeCache[k] = update[k];
|
||||
#endif
|
||||
} while(--k >= 0);
|
||||
l->count++;
|
||||
}
|
||||
|
||||
GSISLValueType GSISLRemoveItemAtIndex(GSISList l, unsigned index)
|
||||
{
|
||||
int k,m;
|
||||
GSISLNode update[GSISLMaxNumberOfLevels];
|
||||
unsigned updateIndexes[GSISLMaxNumberOfLevels];
|
||||
GSISLNode p,q;
|
||||
unsigned depth = 0;
|
||||
GSISLValueType ret;
|
||||
|
||||
k = m = l->level;
|
||||
#if GSISL_CACHE_OPT
|
||||
if (l->indexCache[k] < index)
|
||||
{
|
||||
p = l->nodeCache[k];
|
||||
depth = l->indexCache[k];
|
||||
}
|
||||
else
|
||||
{
|
||||
p = l->header;
|
||||
}
|
||||
#else
|
||||
p = l->header;
|
||||
#endif
|
||||
do
|
||||
{
|
||||
while (q = p->forward[k].next,
|
||||
q != GSISLNil && depth + p->forward[k].delta < index + 1)
|
||||
{
|
||||
depth += p->forward[k].delta;
|
||||
p = q;
|
||||
}
|
||||
update[k] = p;
|
||||
updateIndexes[k] = depth;
|
||||
} while(--k >= 0);
|
||||
|
||||
for (k = 0; k <= m; k++)
|
||||
{
|
||||
p = update[k];
|
||||
#if GSISL_CACHE_OPT
|
||||
l->indexCache[k] = updateIndexes[k];
|
||||
l->nodeCache[k] = update[k];
|
||||
#endif
|
||||
if (p->forward[k].next == q)
|
||||
{
|
||||
p->forward[k].delta = (q->forward[k].next == GSISLNil)
|
||||
? 0
|
||||
: p->forward[k].delta + q->forward[k].delta - 1;
|
||||
|
||||
p->forward[k].next = q->forward[k].next;
|
||||
}
|
||||
else if (p->forward[k].next != GSISLNil)
|
||||
{
|
||||
p->forward[k].delta--;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->forward[k].delta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = q->value;
|
||||
NSZoneFree(l->zone,q);
|
||||
|
||||
/* if header points to nil, decrement the list level */
|
||||
while (l->header->forward[m].next == GSISLNil && m > 0 )
|
||||
{
|
||||
l->header->forward[m].delta = 0;
|
||||
m--;
|
||||
}
|
||||
l->level = m;
|
||||
l->count--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
GSISLValueType GSISLItemAtIndex(GSISList l, unsigned index)
|
||||
{
|
||||
int k;
|
||||
unsigned depth = 0;
|
||||
GSISLNode p,q;
|
||||
|
||||
k = l->level;
|
||||
#if GSISL_CACHE_OPT
|
||||
if (l->indexCache[k] < index)
|
||||
{
|
||||
p = l->nodeCache[k];
|
||||
depth = l->indexCache[k];
|
||||
}
|
||||
else
|
||||
{
|
||||
p = l->header;
|
||||
}
|
||||
#else
|
||||
p = l->header;
|
||||
#endif
|
||||
|
||||
do
|
||||
{
|
||||
while (q = p->forward[k].next, q != GSISLNil && depth + p->forward[k].delta < index + 1)
|
||||
{
|
||||
depth += p->forward[k].delta;
|
||||
p = q;
|
||||
}
|
||||
#if GSISL_CACHE_OPT
|
||||
l->nodeCache[k] = p;
|
||||
l->indexCache[k] = depth;
|
||||
#endif
|
||||
} while(--k >= 0);
|
||||
|
||||
return(q->value);
|
||||
}
|
||||
|
||||
GSISLValueType GSISLReplaceItemAtIndex(GSISList l, GSISLValueType newVal, unsigned index)
|
||||
{
|
||||
int k;
|
||||
unsigned depth = 0;
|
||||
GSISLNode p,q;
|
||||
GSISLValueType ret;
|
||||
|
||||
k = l->level;
|
||||
|
||||
#if GSISL_CACHE_OPT
|
||||
if (l->indexCache[k] < index)
|
||||
{
|
||||
p = l->nodeCache[k];
|
||||
depth = l->indexCache[k];
|
||||
}
|
||||
else
|
||||
{
|
||||
p = l->header;
|
||||
}
|
||||
#else
|
||||
p = l->header;
|
||||
#endif
|
||||
|
||||
do
|
||||
{
|
||||
while (q = p->forward[k].next,
|
||||
q != GSISLNil && depth + p->forward[k].delta < index + 1)
|
||||
{
|
||||
depth += p->forward[k].delta;
|
||||
p = q;
|
||||
}
|
||||
#if GSISL_CACHE_OPT
|
||||
l->indexCache[k] = depth;
|
||||
l->nodeCache[k] = p;
|
||||
#endif
|
||||
} while(--k >= 0);
|
||||
|
||||
ret = q->value;
|
||||
q->value = newVal;
|
||||
return ret;
|
||||
}
|
69
GSSkipMutableArray.h
Normal file
69
GSSkipMutableArray.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Matt Rice <ratmice@yahoo.com>
|
||||
Date: 2006
|
||||
|
||||
This file is part of the Performance 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
*/
|
||||
#include <Foundation/NSArray.h>
|
||||
#include <GSIndexedSkipList.h>
|
||||
|
||||
/**
|
||||
<p>A NSMutableArray subclass which uses a skip list variant for it's underlying
|
||||
data structure.</p>
|
||||
|
||||
<p>while a skip list is typically sorted and represents a dictionary.
|
||||
the indexed skip list is sorted by index and maintains deltas to represent
|
||||
the distance between linked nodes.</p>
|
||||
|
||||
<p><code>the underlying data structure looks much like the figure below:<br/>
|
||||
index -> HEAD 1 2 3 4 5 6 TAIL<br/>
|
||||
5| ---------------------> # ------> #<br/>
|
||||
3| -----------> 2 ------> # ------> #<br/>
|
||||
1| -> 1 -> 1 -> 1 -> 1 -> 1 -> # -> #<br/></code></p>
|
||||
|
||||
<p>where the numbers represent how many indexes it is to the next node
|
||||
of the appropriate level. The bottom level always points to the next node.</p>
|
||||
|
||||
<p>finding a specific index starts at the top level, until the current
|
||||
depth + the next nodes delta is larger than wanted index, then it goes down
|
||||
1 level, and repeats until it finds the wanted index.</p>
|
||||
|
||||
<p>addition and removal of indexes requires an update of the deltas of nodes
|
||||
which begin before, and end after the wanted index,
|
||||
these are the places where it goes down a level.</p>
|
||||
|
||||
<p>the rationale behind it was where a linked list based mutable array will
|
||||
quickly add and remove elements, it may perform poorly at accessing any
|
||||
random index (because it must traverse the entire list to get to the index).</p>
|
||||
|
||||
<p>and while a c array based mutable array will perform good at random index
|
||||
access it may perform poorly at adding and removing indexes
|
||||
(because it must move all items after the altered index).</p>
|
||||
|
||||
<p>so while a SkipMutableArray may not outperform a linked list or a c array
|
||||
mutable array at their specific strengths, it attempts to not suffer from
|
||||
either of their weaknesses, at the cost of additional memory overhead..</p>
|
||||
*/
|
||||
@interface SkipMutableArray : NSMutableArray
|
||||
{
|
||||
GSISList l;
|
||||
}
|
||||
|
||||
@end
|
||||
|
251
GSSkipMutableArray.m
Normal file
251
GSSkipMutableArray.m
Normal file
|
@ -0,0 +1,251 @@
|
|||
/**
|
||||
Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Matt Rice <ratmice@yahoo.com>
|
||||
Date: 2006
|
||||
|
||||
This file is part of the Performance 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
*/
|
||||
|
||||
#include "GSSkipMutableArray.h"
|
||||
#include <Foundation/NSException.h>
|
||||
#include <Foundation/NSValue.h>
|
||||
#include <Foundation/NSEnumerator.h>
|
||||
|
||||
@interface SkipMutableArray(Private)
|
||||
- (GSISList) _list;
|
||||
@end
|
||||
|
||||
@implementation SkipMutableArray(Private)
|
||||
- (GSISList) _list
|
||||
{
|
||||
return l;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface SkipMutableArrayEnumerator : NSEnumerator
|
||||
{
|
||||
GSISLNode node;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SkipMutableArrayEnumerator
|
||||
- (id) initWithArray:(NSArray *)arr
|
||||
{
|
||||
if (![arr isKindOfClass:[SkipMutableArray class]])
|
||||
{
|
||||
[[NSException exceptionWithName:NSInternalInconsistencyException reason:@"not a SkipMutableArray" userInfo:nil] raise];
|
||||
}
|
||||
self = [super init];
|
||||
node = [(SkipMutableArray *)arr _list]->header->forward[0].next;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) nextObject
|
||||
{
|
||||
id foo = node->value;
|
||||
if (node == GSISLNil)
|
||||
return nil;
|
||||
node = node->forward[0].next;
|
||||
return foo;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SkipMutableArray : NSMutableArray
|
||||
- (void) _raiseRangeExceptionWithIndex: (unsigned)index from: (SEL)sel
|
||||
{
|
||||
NSDictionary *info;
|
||||
NSException *exception;
|
||||
NSString *reason;
|
||||
|
||||
info = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithUnsignedInt: index], @"Index",
|
||||
[NSNumber numberWithUnsignedInt: l->count], @"Count",
|
||||
self, @"Array", nil, nil];
|
||||
|
||||
reason = [NSString stringWithFormat: @"Index %d is out of range %d (in '%@')", index, l->count, NSStringFromSelector(sel)];
|
||||
|
||||
exception = [NSException exceptionWithName: NSRangeException
|
||||
reason: reason
|
||||
userInfo: info];
|
||||
[exception raise];
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
GSISLInitialize();
|
||||
}
|
||||
|
||||
|
||||
- (id) initWithObjects:(id *)objects count:(unsigned) count
|
||||
{
|
||||
int i;
|
||||
self = [super init];
|
||||
|
||||
if (!self) return nil;
|
||||
|
||||
l = GSISLInitList([self zone]);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
GSISLInsertItemAtIndex(l, RETAIN(objects[i]), i);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) return nil;
|
||||
|
||||
l = GSISLInitList([self zone]);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
GSISLNode p,q;
|
||||
|
||||
p = l->header->forward[0].next;
|
||||
do
|
||||
{
|
||||
q = p->forward[0].next;
|
||||
RELEASE(p->value);
|
||||
NSZoneFree(l->zone,p);
|
||||
p = q;
|
||||
} while (p != GSISLNil);
|
||||
NSZoneFree(l->zone, l->header);
|
||||
NSZoneFree(l->zone, l);
|
||||
}
|
||||
|
||||
- (void) insertObject:(id)object atIndex:(unsigned)index
|
||||
{
|
||||
if (index > l->count)
|
||||
{
|
||||
[self _raiseRangeExceptionWithIndex: index from: _cmd];
|
||||
}
|
||||
|
||||
GSISLInsertItemAtIndex(l, RETAIN(object), index);
|
||||
}
|
||||
|
||||
- (id) objectAtIndex:(unsigned)index
|
||||
{
|
||||
if (index >= l->count)
|
||||
{
|
||||
[self _raiseRangeExceptionWithIndex: index from: _cmd];
|
||||
}
|
||||
|
||||
return GSISLItemAtIndex(l, index);
|
||||
}
|
||||
|
||||
- (void) removeObjectAtIndex:(unsigned) index
|
||||
{
|
||||
if (index >= l->count)
|
||||
{
|
||||
[self _raiseRangeExceptionWithIndex: index from: _cmd];
|
||||
}
|
||||
|
||||
RELEASE(GSISLRemoveItemAtIndex(l, index));
|
||||
}
|
||||
|
||||
- (void) addObject:(id)obj
|
||||
{
|
||||
GSISLInsertItemAtIndex(l, RETAIN(obj), l->count);
|
||||
}
|
||||
|
||||
- (unsigned) count
|
||||
{
|
||||
return l->count;
|
||||
}
|
||||
|
||||
- (void) replaceObjectAtIndex:(unsigned)index withObject:(id)obj
|
||||
{
|
||||
RELEASE(GSISLReplaceItemAtIndex(l, RETAIN(obj), index));
|
||||
}
|
||||
|
||||
- (NSEnumerator*) objectEnumerator
|
||||
{
|
||||
id e;
|
||||
|
||||
e = [SkipMutableArrayEnumerator allocWithZone: NSDefaultMallocZone()];
|
||||
e = [e initWithArray: self];
|
||||
return AUTORELEASE(e);
|
||||
}
|
||||
/* returns an in an NSString suitable for running through graphviz,
|
||||
* with the graph named 'graphName'
|
||||
*/
|
||||
- (NSString *) _makeGraphOfInternalLayoutNamed:(NSString *)graphName
|
||||
{
|
||||
GSISLNode p;
|
||||
unsigned k, i;
|
||||
|
||||
p = l->header;
|
||||
k = l->level;
|
||||
NSMutableString *graph = [[NSMutableString alloc] init];
|
||||
[graph appendString:[NSString stringWithFormat:@"digraph %@ {\n", graphName]];
|
||||
[graph appendString:@"graph [rankdir = LR];\n"];
|
||||
[graph appendString:@"node [shape = record];\n"];
|
||||
NSMutableDictionary *values = [[NSMutableDictionary alloc] init];
|
||||
NSMutableArray *edges = [[NSMutableArray alloc] init];
|
||||
NSArray *tmp;
|
||||
[values setObject:[NSMutableString stringWithFormat:@"\"%p\" [label = \"%p (NIL) |{ <delta0> 0 | <forward0> }",GSISLNil, GSISLNil] forKey:[NSString stringWithFormat:@"%p", GSISLNil]];
|
||||
for (k = 0; k < l->level + 1; k++)
|
||||
{
|
||||
for (p = l->header; p != GSISLNil; p = p->forward[k].next)
|
||||
{
|
||||
NSString *value = [NSString stringWithFormat:@"%p", p];
|
||||
NSMutableString *foo = [values objectForKey:value];
|
||||
|
||||
if (foo == nil)
|
||||
{
|
||||
foo = [[NSMutableString alloc] init];
|
||||
[foo appendString:[NSString stringWithFormat:@"\"%p\" [label = \"%p%@ |{ <delta%i> %i | <forward%i> }", p, p, p == l->header ? @"(HEADER)" : @"", k, p->forward[k].delta, k]];
|
||||
if (p != GSISLNil)
|
||||
[edges addObject:[NSString stringWithFormat:@"\"%p\":forward%i -> \"%p\":delta%i;\n",p,k, p->forward[k].next,p->forward[k].next == GSISLNil ? 0 : k]];
|
||||
[values setObject:foo forKey:value];
|
||||
RELEASE(foo);
|
||||
}
|
||||
else
|
||||
{
|
||||
[foo appendString:[NSString stringWithFormat:@"|{ <delta%i> %i | <forward%i> }", k, p->forward[k].delta, k]];
|
||||
if (p != GSISLNil)
|
||||
[edges addObject:[NSString stringWithFormat:@"\"%p\":forward%i -> \"%p\":delta%i;\n",p,k, p->forward[k].next, p->forward[k].next == GSISLNil ? 0 : k]];
|
||||
[values setObject:foo forKey:value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmp = [values allKeys];
|
||||
for (i = 0; i < [tmp count]; i++)
|
||||
{
|
||||
[graph appendString:[values objectForKey:[tmp objectAtIndex:i]]];
|
||||
[graph appendString:@"\"];\n"];
|
||||
}
|
||||
for (i = 0; i < [edges count]; i++)
|
||||
{
|
||||
[graph appendString:[edges objectAtIndex:i]];
|
||||
}
|
||||
[graph appendString:@"}\n"];
|
||||
RELEASE(values);
|
||||
RELEASE(edges);
|
||||
return AUTORELEASE(graph);
|
||||
}
|
||||
|
||||
|
||||
@end
|
Loading…
Reference in a new issue