diff --git a/ChangeLog b/ChangeLog index 9b6def9..1fc524c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2015-07-29 Richard Frith-Macdonald + + * GSLinkedList.h: + * GSLinkedList.m: + Add GSLinkedStore for a list which controls its own links and stores + unused links to avoid having to create/destroy link objects + repeatedly. + 2015-07-17 Niels Grewe * GSFIFO.m: Implement methods that allow waiting on an empty diff --git a/GSLinkedList.h b/GSLinkedList.h index 5ff8c69..63fd4d1 100644 --- a/GSLinkedList.h +++ b/GSLinkedList.h @@ -254,7 +254,18 @@ extern GSListLink* GSLinkedListFindIdentical(NSObject *object, GSLinkedList *list, GSListLink *from, BOOL back); +/** Returns the first object in the list. + */ +static inline id +GSLinkedListFirstObject(GSLinkedList *list) +{ + if (nil == list->head) + return nil; + return list->head->item; +} + /** Inserts link immediately after at.
+ * If at is nil, inserts at the end of the list (link becomes tail).
* Updates the head, tail and count variables of list.
* Does not retain link. */ @@ -262,12 +273,23 @@ extern void GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at); /** Inserts link immediately before at.
+ * If at is nil, inserts at the start of the list (link becomes head).
* Updates the head, tail and count variables of list.
* Does not retain link. */ extern void GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at); +/** Returns the last object in the list. + */ +static inline id +GSLinkedListLastObject(GSLinkedList *list) +{ + if (nil == list->tail) + return nil; + return list->tail->item; +} + /** Moves the link to the head of the list if it is not already there. */ extern void @@ -285,4 +307,80 @@ GSLinkedListMoveToTail(GSListLink *link, GSLinkedList *list); extern void GSLinkedListRemove(GSListLink *link, GSLinkedList *list); + + +/** This class extends GSLinkedList by providing storage for unused links + * and re-using those links when a new link is needed.
+ * This avoids the overhead of allocating/deallocating links and provides + * an API more like a mutable array. + */ +@interface GSLinkStore : GSLinkedList +{ +@public + GSListLink *free; /** The unused links */ +} + +/** Adds an object at the end of the list (calls -insertObject:after:). + */ +- (void) addObject: (id)anObject; + +/** Returns the first object in the list or nil if the list is empty. + */ +- (id) firstObject; + +/** Inserts anObject immediately after the specified link. If at is nil + * the object is inserted at the end of the list. + */ +- (void) insertObject: (id)anObject after: (GSListLink*)at; + +/** Inserts anObject immediately before the specified link. If at is nil + * the object is inserted at the start of the list. + */ +- (void) insertObject: (id)anObject before: (GSListLink*)at; + +/** Returns the last object in the list or nil if the list is empty. + */ +- (id) lastObject; + +/** Removes any unused links from the list (to release the memory they + * occupied). + */ +- (void) purge; + +/** Removes the first object from the list (or does nothing if the list + * is empty). + */ +- (void) removeFirstObject; + +/** Removes the last object from the list (or does nothing if the list + * is empty). + */ +- (void) removeLastObject; +@end + +/** Adds the object at the end of the list. + */ +extern void +GSLinkStoreAddObject(GSLinkStore *list, NSObject *anObject); + +/** Adds the object to the list after the specified link.
+ * Calls GSLinkedListInsertAfter(). + */ +extern void +GSLinkStoreInsertObjectAfter( + NSObject *anObject, GSLinkStore *list, GSListLink *at); + +/** Adds the object to the list before the specified link.
+ * Calls GSLinkedListInsertBefore(). + */ +extern void +GSLinkStoreInsertObjectBefore( + NSObject *anObject, GSLinkStore *list, GSListLink *at); + +/** Removes the object held in the specified link.
+ * If at is nil or is not owned by the list, this does nothing. + */ +extern void +GSLinkStoreRemoveObjectAt(GSLinkStore *list, GSListLink *at); + #endif diff --git a/GSLinkedList.m b/GSLinkedList.m index 2882972..3dea131 100644 --- a/GSLinkedList.m +++ b/GSLinkedList.m @@ -361,31 +361,6 @@ GSLinkedListFindIdentical(NSObject *object, GSLinkedList *list, return nil; } -void -GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at) -{ - if (nil == list->head) - { - list->head = list->tail = link; - } - else - { - link->previous = at->previous; - if (nil == link->previous) - { - list->head = link; - } - else - { - link->previous->next = link; - } - at->previous = link; - link->next = at; - } - link->owner = list; - list->count++; -} - void GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at) { @@ -395,6 +370,10 @@ GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at) } else { + if (nil == at) + { + at = list->tail; + } link->next = at->next; if (nil == link->next) { @@ -411,6 +390,35 @@ GSLinkedListInsertAfter(GSListLink *link, GSLinkedList *list, GSListLink *at) list->count++; } +void +GSLinkedListInsertBefore(GSListLink *link, GSLinkedList *list, GSListLink *at) +{ + if (nil == list->head) + { + list->head = list->tail = link; + } + else + { + if (nil == at) + { + at = list->head; + } + link->previous = at->previous; + if (nil == link->previous) + { + list->head = link; + } + else + { + link->previous->next = link; + } + at->previous = link; + link->next = at; + } + link->owner = list; + list->count++; +} + void GSLinkedListRemove(GSListLink *link, GSLinkedList *list) { @@ -487,3 +495,128 @@ GSLinkedListMoveToTail(GSListLink *link, GSLinkedList *list) } } +@implementation GSLinkStore + +- (void) addObject: (id)anObject +{ + GSLinkStoreInsertObjectAfter(anObject, self, tail); +} + +- (void) dealloc +{ + [self empty]; + [self purge]; + [super dealloc]; +} + +- (void) empty +{ + while (nil != head) + { + GSLinkStoreRemoveObjectAt(self, head); + } +} + +- (id) firstObject +{ + return GSLinkedListFirstObject(self); +} + +- (void) insertObject: (id)anObject after: (GSListLink*)at +{ + GSLinkStoreInsertObjectAfter(anObject, self, at); +} + +- (void) insertObject: (id)anObject before: (GSListLink*)at +{ + GSLinkStoreInsertObjectBefore(anObject, self, at); +} + +- (id) lastObject +{ + return GSLinkedListLastObject(self); +} + +- (void) purge +{ + while (nil != free) + { + GSListLink *link = free; + + free = link->next; + link->next = nil; + link->owner = nil; + [link release]; + } +} + +- (void) removeFirstObject +{ + if (nil != head) + { + GSLinkStoreRemoveObjectAt(self, head); + } +} + +- (void) removeLastObject +{ + if (nil != tail) + { + GSLinkStoreRemoveObjectAt(self, tail); + } +} + +@end + +void +GSLinkStoreInsertObjectAfter( + NSObject *anObject, GSLinkStore *list, GSListLink *at) +{ + GSListLink *link = list->free; + + if (nil == link) + { + link = [GSListLink new]; + link->owner = list; + } + else + { + list->free = link->next; + link->next = nil; + } + link->item = [anObject retain]; + GSLinkedListInsertAfter(link, list, list->tail); +} + +void +GSLinkStoreInsertObjectBefore( + NSObject *anObject, GSLinkStore *list, GSListLink *at) +{ + GSListLink *link = list->free; + + if (nil == link) + { + link = [GSListLink new]; + link->owner = list; + } + else + { + list->free = link->next; + link->next = nil; + } + link->item = [anObject retain]; + GSLinkedListInsertBefore(link, list, list->head); +} + +void +GSLinkStoreRemoveObjectAt(GSLinkStore *list, GSListLink *at) +{ + if (nil != at && at->owner == list) + { + GSLinkedListRemove(at, list); + [at->item release]; + at->next = list->free; + list->free = at; + } +} +