diff --git a/objc/objc_association.m b/objc/objc_association.m index 9b9bab87..e13dde30 100644 --- a/objc/objc_association.m +++ b/objc/objc_association.m @@ -15,7 +15,8 @@ typedef struct { objc_AssociationPolicy policy; } AssociationObjectEntry; -#define AssociationObjectEntrySize 4 +#define BucketsInitialSize 509 // Some prime size (and as a bonus, next three n*2+1 are prime too) +#define AssociationObjectEntrySize 5 #define HASHPTR(p) (((unsigned int)p)>>5) @@ -31,18 +32,11 @@ typedef struct AssociationTable { AssociationHashBucket **buckets; } AssociationTable; -typedef struct { - AssociationTable *table; - unsigned int i; - struct AssociationHashBucket *j; -} NSHashEnumerator; - - AssociationTable *CreateAssociationTable(unsigned int capacity) { AssociationTable *table=malloc(sizeof(AssociationTable)); table->count=0; - table->nBuckets=(capacity<4)?4:capacity; + table->nBuckets=(capacity<5)?5:capacity; table->buckets=calloc(table->nBuckets,sizeof(AssociationHashBucket *)); return table; @@ -65,31 +59,48 @@ void AssociationTableInsert(AssociationTable *table,id key, AssociationObjectEnt unsigned int i=hash%table->nBuckets; AssociationHashBucket *j; - for(j=table->buckets[i];j!=NULL;j=j->next) + for(j=table->buckets[i];j!=NULL;j=j->next) { if(j->key==key){ - id oldKey=j->key; AssociationObjectEntry *oldValue=j->value; - j->key=key; - j->value=value; + j->value=value; + + free(oldValue); return; } - - if(table->count>=table->nBuckets){ + } + int newSize = 0; + if (table->count>=table->nBuckets) { + // Expand the buckets size to limit collisions + newSize=table->nBuckets*2+1; // Let"s use odd size + } else if (table->count > BucketsInitialSize && table->count < table->nBuckets/2) { + // Compact the table - plenty of free room + newSize=table->nBuckets/2; + if (newSize%2) { + // Let"s use odd size + newSize+=1; + } + } + if(newSize != 0){ unsigned int nBuckets=table->nBuckets; - AssociationHashBucket **buckets=table->buckets,*next; + AssociationHashBucket **buckets=table->buckets; - table->nBuckets=nBuckets*2; + table->nBuckets=newSize; table->buckets=calloc(table->nBuckets,sizeof(AssociationHashBucket *)); - for(i=0;inBuckets; + // Get all of the existing buckets and place them into the new list + for(i=0;ikey)%table->nBuckets; - next=j->next; + AssociationHashBucket *next = j->next; j->next=table->buckets[newi]; table->buckets[newi]=j; + + j = next; } + } free(buckets); i=hash%table->nBuckets; } @@ -156,7 +167,7 @@ void AssociationTableRemove(AssociationTable *table,id key){ AssociationObjectEntry *entry = j->value; for (int i = 0; i < AssociationObjectEntrySize; i++) { - AssociationObjectEntry *e = (AssociationObjectEntry *)entry + i; + AssociationObjectEntry *e = entry + i; while (e) { switch (e->policy) { case OBJC_ASSOCIATION_ASSIGN: @@ -176,9 +187,12 @@ void AssociationTableRemove(AssociationTable *table,id key){ objectsToRelease[releaseCount++] = e->object; break; } - void *prevEntry = e; + AssociationObjectEntry *currentEntry = e; e = e->nextEntry; - free(prevEntry); + // Don't free the first entry of the list - it's part of the "entry" block that will be freed after the loop + if (currentEntry != entry + i) { + free(currentEntry); + } } } @@ -215,7 +229,7 @@ void objc_setAssociatedObject(id object, const void *key, id value, objc_Associa AssociationSpinLockLock(&AssociationLock); if (associationTable == NULL) { - associationTable = CreateAssociationTable(512); + associationTable = CreateAssociationTable(BucketsInitialSize); } AssociationObjectEntry *objectTable = AssociationTableGet(associationTable, object);