Index: src/OFBlock.m ================================================================== --- src/OFBlock.m +++ src/OFBlock.m @@ -392,16 +392,10 @@ selector: _cmd]; } - init { - @throw [OFNotImplementedException exceptionWithClass: isa - selector: _cmd]; -} - -- (void)addMemoryToPool: (void*)ptr -{ @throw [OFNotImplementedException exceptionWithClass: isa selector: _cmd]; } - (void*)allocMemoryWithSize: (size_t)size Index: src/OFConstantString.m ================================================================== --- src/OFConstantString.m +++ src/OFConstantString.m @@ -53,15 +53,10 @@ { @throw [OFNotImplementedException exceptionWithClass: self selector: _cmd]; } -- (void)addMemoryToPool: (void*)ptr -{ - @throw [OFNotImplementedException exceptionWithClass: isa - selector: _cmd]; -} - (void*)allocMemoryWithSize: (size_t)size { @throw [OFNotImplementedException exceptionWithClass: isa selector: _cmd]; @@ -179,16 +174,10 @@ { @throw [OFNotImplementedException exceptionWithClass: self selector: _cmd]; } -- (void)addMemoryToPool: (void*)ptr -{ - @throw [OFNotImplementedException exceptionWithClass: isa - selector: _cmd]; -} - - (void*)allocMemoryWithSize: (size_t)size { @throw [OFNotImplementedException exceptionWithClass: isa selector: _cmd]; } Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -474,20 +474,10 @@ * * \return A description for the object */ - (OFString*)description; -/** - * \brief Adds a pointer to the object's memory pool. - * - * This is useful to add memory allocated by functions such as asprintf to the - * pool so it gets free'd automatically when the object is deallocated. - * - * \param pointer A pointer to add to the memory pool - */ -- (void)addMemoryToPool: (void*)pointer; - /** * \brief Allocates memory and stores it in the object's memory pool. * * It will be free'd automatically when the object is deallocated. * Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -59,17 +59,21 @@ #elif defined(OF_THREADS) # import "threading.h" #endif struct pre_ivar { - int32_t retainCount; - void **memoryChunks; - unsigned int memoryChunksSize; + int32_t retainCount; + struct pre_mem *firstMem, *lastMem; #if !defined(OF_ATOMIC_OPS) && defined(OF_THREADS) of_spinlock_t retainCountSpinlock; #endif }; + +struct pre_mem { + id owner; + struct pre_mem *prev, *next; +}; /* Hopefully no arch needs more than 16 bytes padding */ #ifndef __BIGGEST_ALIGNMENT__ # define __BIGGEST_ALIGNMENT__ 16 #endif @@ -76,10 +80,14 @@ #define PRE_IVAR_ALIGN ((sizeof(struct pre_ivar) + \ (__BIGGEST_ALIGNMENT__ - 1)) & ~(__BIGGEST_ALIGNMENT__ - 1)) #define PRE_IVAR ((struct pre_ivar*)(void*)((char*)self - PRE_IVAR_ALIGN)) +#define PRE_MEM_ALIGN ((sizeof(struct pre_mem) + \ + (__BIGGEST_ALIGNMENT__ - 1)) & ~(__BIGGEST_ALIGNMENT__ - 1)) +#define PRE_MEM(mem) ((struct pre_mem*)(void*)((char*)mem - PRE_MEM_ALIGN)) + #ifdef OF_OLD_GNU_RUNTIME extern void __objc_update_dispatch_table_for_class(Class); #endif static struct { @@ -236,12 +244,10 @@ if ((instance = calloc(instanceSize + PRE_IVAR_ALIGN, 1)) == NULL) { alloc_failed_exception.isa = [OFAllocFailedException class]; @throw (OFAllocFailedException*)&alloc_failed_exception; } - ((struct pre_ivar*)instance)->memoryChunks = NULL; - ((struct pre_ivar*)instance)->memoryChunksSize = 0; ((struct pre_ivar*)instance)->retainCount = 1; #if !defined(OF_ATOMIC_OPS) && defined(OF_THREADS) if (!of_spinlock_new( &((struct pre_ivar*)instance)->retainCountSpinlock)) { @@ -751,63 +757,33 @@ { /* Classes containing data should reimplement this! */ return [OFString stringWithFormat: @"<%@: %p>", [self className], self]; } -- (void)addMemoryToPool: (void*)pointer -{ - void **memoryChunks; - unsigned int memoryChunksSize; - - memoryChunksSize = PRE_IVAR->memoryChunksSize + 1; - - if (UINT_MAX - PRE_IVAR->memoryChunksSize < 1 || - memoryChunksSize > UINT_MAX / sizeof(void*)) - @throw [OFOutOfRangeException exceptionWithClass: isa]; - - if ((memoryChunks = realloc(PRE_IVAR->memoryChunks, - memoryChunksSize * sizeof(void*))) == NULL) - @throw [OFOutOfMemoryException - exceptionWithClass: isa - requestedSize: memoryChunksSize]; - - PRE_IVAR->memoryChunks = memoryChunks; - PRE_IVAR->memoryChunks[PRE_IVAR->memoryChunksSize] = pointer; - PRE_IVAR->memoryChunksSize = memoryChunksSize; -} - -- (void*)allocMemoryWithSize: (size_t)size -{ - void *pointer, **memoryChunks; - unsigned int memoryChunksSize; - - if (size == 0) - return NULL; - - memoryChunksSize = PRE_IVAR->memoryChunksSize + 1; - - if (UINT_MAX - PRE_IVAR->memoryChunksSize == 0 || - memoryChunksSize > UINT_MAX / sizeof(void*)) - @throw [OFOutOfRangeException exceptionWithClass: isa]; - - if ((pointer = malloc(size)) == NULL) - @throw [OFOutOfMemoryException exceptionWithClass: isa - requestedSize: size]; - - if ((memoryChunks = realloc(PRE_IVAR->memoryChunks, - memoryChunksSize * sizeof(void*))) == NULL) { - free(pointer); - @throw [OFOutOfMemoryException - exceptionWithClass: isa - requestedSize: memoryChunksSize]; - } - - PRE_IVAR->memoryChunks = memoryChunks; - PRE_IVAR->memoryChunks[PRE_IVAR->memoryChunksSize] = pointer; - PRE_IVAR->memoryChunksSize = memoryChunksSize; - - return pointer; +- (void*)allocMemoryWithSize: (size_t)size +{ + void *pointer; + struct pre_mem *preMem; + + if (size > SIZE_MAX - PRE_IVAR_ALIGN) + @throw [OFOutOfRangeException exceptionWithClass: isa]; + + if ((pointer = malloc(PRE_MEM_ALIGN + size)) == NULL) + @throw [OFOutOfMemoryException exceptionWithClass: isa + requestedSize: size]; + preMem = pointer; + + preMem->owner = self; + preMem->prev = PRE_IVAR->lastMem; + preMem->next = NULL; + + if (PRE_IVAR->lastMem != NULL) + PRE_IVAR->lastMem->next = preMem; + + PRE_IVAR->lastMem = preMem; + + return (char*)pointer + PRE_MEM_ALIGN; } - (void*)allocMemoryForNItems: (size_t)nItems ofSize: (size_t)size { @@ -821,37 +797,44 @@ } - (void*)resizeMemory: (void*)pointer toSize: (size_t)size { - void **iter; + void *new; + struct pre_mem *preMem; if (pointer == NULL) return [self allocMemoryWithSize: size]; if (size == 0) { [self freeMemory: pointer]; return NULL; } - iter = PRE_IVAR->memoryChunks + PRE_IVAR->memoryChunksSize; - - while (iter-- > PRE_IVAR->memoryChunks) { - if (OF_UNLIKELY(*iter == pointer)) { - if (OF_UNLIKELY((pointer = realloc(pointer, - size)) == NULL)) - @throw [OFOutOfMemoryException - exceptionWithClass: isa - requestedSize: size]; - - *iter = pointer; - return pointer; - } - } - - @throw [OFMemoryNotPartOfObjectException exceptionWithClass: isa - pointer: pointer]; + if (PRE_MEM(pointer)->owner != self) + @throw [OFMemoryNotPartOfObjectException + exceptionWithClass: isa + pointer: pointer]; + + if ((new = realloc(PRE_MEM(pointer), PRE_MEM_ALIGN + size)) == NULL) + @throw [OFOutOfMemoryException exceptionWithClass: isa + requestedSize: size]; + preMem = new; + + if (preMem != PRE_MEM(pointer)) { + if (preMem->prev != NULL) + preMem->prev->next = preMem; + if (preMem->next != NULL) + preMem->next->prev = preMem; + + if (PRE_IVAR->firstMem == PRE_MEM(pointer)) + PRE_IVAR->firstMem = preMem; + if (PRE_IVAR->lastMem == PRE_MEM(pointer)) + PRE_IVAR->lastMem = preMem; + } + + return (char*)new + PRE_MEM_ALIGN; } - (void*)resizeMemory: (void*)pointer toNItems: (size_t)nItems ofSize: (size_t)size @@ -872,56 +855,32 @@ toSize: nItems * size]; } - (void)freeMemory: (void*)pointer { - void **iter, *last, **memoryChunks; - unsigned int i, memoryChunksSize; - if (pointer == NULL) return; - iter = PRE_IVAR->memoryChunks + PRE_IVAR->memoryChunksSize; - i = PRE_IVAR->memoryChunksSize; - - while (iter-- > PRE_IVAR->memoryChunks) { - i--; - - if (OF_UNLIKELY(*iter == pointer)) { - memoryChunksSize = PRE_IVAR->memoryChunksSize - 1; - last = PRE_IVAR->memoryChunks[memoryChunksSize]; - - assert(PRE_IVAR->memoryChunksSize != 0 && - memoryChunksSize <= UINT_MAX / sizeof(void*)); - - if (OF_UNLIKELY(memoryChunksSize == 0)) { - free(pointer); - free(PRE_IVAR->memoryChunks); - - PRE_IVAR->memoryChunks = NULL; - PRE_IVAR->memoryChunksSize = 0; - - return; - } - - free(pointer); - PRE_IVAR->memoryChunks[i] = last; - PRE_IVAR->memoryChunksSize = memoryChunksSize; - - if (OF_UNLIKELY((memoryChunks = realloc( - PRE_IVAR->memoryChunks, memoryChunksSize * - sizeof(void*))) == NULL)) - return; - - PRE_IVAR->memoryChunks = memoryChunks; - - return; - } - } - - @throw [OFMemoryNotPartOfObjectException exceptionWithClass: isa - pointer: pointer]; + if (PRE_MEM(pointer)->owner != self) + @throw [OFMemoryNotPartOfObjectException + exceptionWithClass: isa + pointer: pointer]; + + if (PRE_MEM(pointer)->prev != NULL) + PRE_MEM(pointer)->prev->next = PRE_MEM(pointer)->next; + if (PRE_MEM(pointer)->next != NULL) + PRE_MEM(pointer)->next->prev = PRE_MEM(pointer)->prev; + + if (PRE_IVAR->firstMem == PRE_MEM(pointer)) + PRE_IVAR->firstMem = PRE_MEM(pointer)->next; + if (PRE_IVAR->lastMem == PRE_MEM(pointer)) + PRE_IVAR->lastMem = PRE_MEM(pointer)->prev; + + /* To detect double-free */ + PRE_MEM(pointer)->owner = nil; + + free(PRE_MEM(pointer)); } - retain { #if defined(OF_ATOMIC_OPS) @@ -989,11 +948,11 @@ - (void)dealloc { Class class; void (*last)(id, SEL) = NULL; - void **iter; + struct pre_mem *iter; for (class = isa; class != Nil; class = class_getSuperclass(class)) { void (*destruct)(id, SEL); if ([class instancesRespondToSelector: cxx_destruct]) { @@ -1004,16 +963,18 @@ last = destruct; } else break; } - iter = PRE_IVAR->memoryChunks + PRE_IVAR->memoryChunksSize; - while (iter-- > PRE_IVAR->memoryChunks) - free(*iter); + iter = PRE_IVAR->firstMem; + while (iter != NULL) { + struct pre_mem *next = iter->next; + + free(iter); - if (PRE_IVAR->memoryChunks != NULL) - free(PRE_IVAR->memoryChunks); + iter = next; + } free((char*)self - PRE_IVAR_ALIGN); } /* Required to use properties with the Apple runtime */ @@ -1037,16 +998,10 @@ /* * Those are needed as the root class is the superclass of the root class's * metaclass and thus instance methods can be sent to class objects as well. */ -+ (void)addMemoryToPool: (void*)pointer -{ - @throw [OFNotImplementedException exceptionWithClass: self - selector: _cmd]; -} - + (void*)allocMemoryWithSize: (size_t)size { @throw [OFNotImplementedException exceptionWithClass: self selector: _cmd]; } Index: src/OFString_UTF8.m ================================================================== --- src/OFString_UTF8.m +++ src/OFString_UTF8.m @@ -433,10 +433,11 @@ arguments: (va_list)arguments { self = [super init]; @try { + char *tmp; int cStringLength; if (format == nil) @throw [OFInvalidArgumentException exceptionWithClass: isa @@ -443,32 +444,33 @@ selector: _cmd]; s = [self allocMemoryWithSize: sizeof(*s)]; memset(s, 0, sizeof(*s)); - if ((cStringLength = of_vasprintf(&s->cString, - [format UTF8String], arguments)) == -1) + if ((cStringLength = of_vasprintf(&tmp, [format UTF8String], + arguments)) == -1) @throw [OFInvalidFormatException exceptionWithClass: isa]; s->cStringLength = cStringLength; @try { - switch (of_string_check_utf8(s->cString, - cStringLength, &s->length)) { + switch (of_string_check_utf8(tmp, cStringLength, + &s->length)) { case 1: s->UTF8 = YES; break; case -1: @throw [OFInvalidEncodingException exceptionWithClass: isa]; } - [self addMemoryToPool: s->cString]; - } @catch (id e) { - free(s->cString); - @throw e; + s->cString = [self + allocMemoryWithSize: cStringLength + 1]; + memcpy(s->cString, tmp, cStringLength + 1); + } @finally { + free(tmp); } } @catch (id e) { [self release]; @throw e; } Index: tests/OFObjectTests.m ================================================================== --- tests/OFObjectTests.m +++ tests/OFObjectTests.m @@ -38,49 +38,49 @@ OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFObject *obj = [[[OFObject alloc] init] autorelease]; void *p, *q, *r; OFObject *o; MyObj *m; - - EXPECT_EXCEPTION(@"Detect freeing of memory not allocated by object", - OFMemoryNotPartOfObjectException, [obj freeMemory: (void*)1]) + char *tmp; TEST(@"Allocating 4096 bytes", (p = [obj allocMemoryWithSize: 4096]) != NULL) TEST(@"Freeing memory", R([obj freeMemory: p])) - EXPECT_EXCEPTION(@"Detect freeing of memory twice", - OFMemoryNotPartOfObjectException, [obj freeMemory: p]) - TEST(@"Allocating and freeing 4096 bytes 3 times", (p = [obj allocMemoryWithSize: 4096]) != NULL && (q = [obj allocMemoryWithSize: 4096]) != NULL && (r = [obj allocMemoryWithSize: 4096]) != NULL && R([obj freeMemory: p]) && R([obj freeMemory: q]) && R([obj freeMemory: r])) + tmp = [self allocMemoryWithSize: 1024]; + EXPECT_EXCEPTION(@"Detect freeing of memory not allocated by object", + OFMemoryNotPartOfObjectException, [obj freeMemory: tmp]) + EXPECT_EXCEPTION(@"Detect out of memory on alloc", - OFOutOfMemoryException, [obj allocMemoryWithSize: SIZE_MAX]) + OFOutOfMemoryException, [obj allocMemoryWithSize: SIZE_MAX - 128]) EXPECT_EXCEPTION(@"Detect out of memory on resize", OFOutOfMemoryException, { p = [obj allocMemoryWithSize: 1]; [obj resizeMemory: p - toSize: SIZE_MAX]; + toSize: SIZE_MAX - 128]; }) [obj freeMemory: p]; TEST(@"Allocate when trying to resize NULL", (p = [obj resizeMemory: NULL toSize: 1024]) != NULL) [obj freeMemory: p]; EXPECT_EXCEPTION(@"Detect resizing of memory not allocated by object", - OFMemoryNotPartOfObjectException, [obj resizeMemory: (void*)1 - toSize: 1024]) + OFMemoryNotPartOfObjectException, [obj resizeMemory: tmp + toSize: 2048]) + [self freeMemory: tmp]; TEST(@"+[description]", [[OFObject description] isEqual: @"OFObject"] && [[MyObj description] isEqual: @"MyObj"])