4 Copyright (C) 1996 Free Software Foundation, Inc.
6 Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
9 This file is part of the GNUstep Database Library.
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public
13 License as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this library; see the file COPYING.LIB.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #import "EOObjectUniquer.h"
30 #import "EOPrimaryKeyDictionary.h"
31 #import "EODatabaseFault.h"
32 #import "EOGenericRecord.h"
34 static unsigned uniquerHash(NSMapTable* table, EOUniquerRecord* rec) {
35 // efficient hash for dictionaries is done in the concrete
36 // dictionaries implementation; dictionaries are allocated
37 // from EOPrimaryKeyDictionary concrete subclasses
38 return ((unsigned)(rec->entity) >> 4) +
39 ((EOPrimaryKeyDictionary*)(rec->pkey))->fastHash;
42 static BOOL uniquerCompare(NSMapTable *table, EOUniquerRecord *rec1,
43 EOUniquerRecord *rec2) {
44 // efficient compare between dictionaries is done in the concrete
45 // dictionaries implementation; dictionaries are allocated
46 // from EOPrimaryKeyDictionary concrete subclasses
47 return (rec1->entity == rec2->entity) && [rec1->pkey fastIsEqual:rec2->pkey];
50 static NSString* uniqDescription(NSMapTable *t, EOUniquerRecord* rec) {
51 return [NSString stringWithFormat:
52 @"<<pkey:%08x entity:%08x object:%08x snapshot:%08x>>",
53 rec->pkey, rec->entity, rec->object, rec->snapshot];
56 static void uniquerRetain(NSMapTable *table, EOUniquerRecord* rec) {
60 static void uniquerRelease(NSMapTable *table, EOUniquerRecord *rec) {
63 if (rec->refCount <= 0) {
64 RELEASE(rec->pkey); rec->pkey = NULL;
65 RELEASE(rec->entity); rec->entity = NULL;
66 RELEASE(rec->snapshot); rec->snapshot = NULL;
67 Free(rec); rec = NULL;
71 static inline EOUniquerRecord *uniquerCreate(id pkey, id entity, id object,
73 EOUniquerRecord *rec = NULL;
75 rec = (EOUniquerRecord *)Malloc(sizeof(EOUniquerRecord));
77 rec->pkey = RETAIN(pkey);
78 rec->entity = RETAIN(entity);
80 rec->snapshot = RETAIN(snapshot);
85 static void uniquerNoAction(NSMapTable * t, const void *_object) {
88 static NSMapTableKeyCallBacks uniquerKeyMapCallbacks = {
89 (unsigned(*)(NSMapTable *, const void *))uniquerHash,
90 (BOOL(*)(NSMapTable *, const void *, const void *))uniquerCompare,
91 (void (*)(NSMapTable *, const void *))uniquerNoAction,
92 (void (*)(NSMapTable *, void *))uniquerNoAction,
93 (NSString *(*)(NSMapTable *, const void *))uniqDescription,
97 static NSMapTableValueCallBacks uniquerValueMapCallbacks = {
98 (void (*)(NSMapTable *, const void *))uniquerRetain,
99 (void (*)(NSMapTable *, void *))uniquerRelease,
100 (NSString *(*)(NSMapTable *, const void *))uniqDescription
103 static int initialHashSize = 1021;
105 @implementation EOObjectUniquer
107 static NSMutableArray *uniquerExtent = nil;
108 static NSRecursiveLock *lock = nil;
111 static BOOL isInitialized = NO;
112 if (!isInitialized) {
115 uniquerExtent = [[NSMutableArray alloc] initWithCapacity:16];
116 // THREAD: lock = [[NSRecursiveLock alloc] init];
120 static inline void _addUniquerInstance(EOObjectUniquer *_uniquer) {
122 [uniquerExtent addObject:[NSValue valueWithNonretainedObject:_uniquer]];
125 static inline void _removeUniquerInstance(EOObjectUniquer *_uniquer) {
130 for (i = [uniquerExtent count] - 1; i >= 0; i--) {
131 EOObjectUniquer *uniquer;
133 uniquer = [[uniquerExtent objectAtIndex:i] nonretainedObjectValue];
134 if (uniquer == _uniquer) {
135 [uniquerExtent removeObjectAtIndex:i];
143 // Initializing a uniquing dictionary
146 self->primaryKeyToRec = NSCreateMapTable(uniquerKeyMapCallbacks,
147 uniquerValueMapCallbacks,
149 #if LIB_FOUNDATION_LIBRARY
150 self->objectsToRec = NSCreateMapTableInvisibleKeysOrValues
151 (NSNonOwnedPointerMapKeyCallBacks,
152 uniquerValueMapCallbacks, initialHashSize,
155 self->objectsToRec = NSCreateMapTable
156 (NSNonOwnedPointerMapKeyCallBacks,
157 uniquerValueMapCallbacks, initialHashSize);
159 self->keyRecord = uniquerCreate(nil, nil, nil, nil);
161 _addUniquerInstance(self);
167 [self forgetAllObjects];
168 _removeUniquerInstance(self);
170 NSFreeMapTable(self->objectsToRec);
171 NSFreeMapTable(self->primaryKeyToRec);
172 if (self->keyRecord) {
173 Free(self->keyRecord);
174 self->keyRecord = NULL;
179 // Transfer self to parent
181 - (void)transferTo:(EOObjectUniquer *)_dest
182 objects:(BOOL)isKey andSnapshots:(BOOL)isSnap
184 EOUniquerRecord *key = NULL;
185 EOUniquerRecord *rec = NULL;
186 NSMapEnumerator enumerator = NSEnumerateMapTable(primaryKeyToRec);
188 while(NSNextMapEnumeratorPair(&enumerator, (void**)(&key), (void**)(&rec))) {
189 [_dest recordObject:rec->object
190 primaryKey: isKey ? rec->pkey : nil
191 entity: isKey ? rec->entity : nil
192 snapshot: isSnap ? rec->snapshot : nil];
194 [self forgetAllObjects];
199 - (void)forgetObject:(id)_object {
200 EOUniquerRecord *rec = NULL;
205 rec = (EOUniquerRecord *)NSMapGet(self->objectsToRec, _object);
211 NSLog(@"Uniquer[0x%08X]: forget object 0x%08X<%s> entity=%@",
212 self, _object, class_get_class_name(*(Class *)_object),
213 [[_object entity] name]);
217 NSMapRemove(self->primaryKeyToRec, rec);
218 NSMapRemove(self->objectsToRec, _object);
221 - (void)forgetAllObjects {
222 NSResetMapTable(self->primaryKeyToRec);
223 NSResetMapTable(self->objectsToRec);
226 - (void)forgetAllSnapshots {
227 NSMapEnumerator enumerator;
228 EOUniquerRecord *rec = NULL;
231 NSLog(@"uniquer 0x%08X forgetAllSnapshots ..", self);
233 enumerator = NSEnumerateMapTable(self->objectsToRec);
234 while (NSNextMapEnumeratorPair(&enumerator, (void**)(&key), (void**)(&rec))) {
235 RELEASE(rec->snapshot);
240 - (id)objectForPrimaryKey:(NSDictionary *)_key entity:(EOEntity *)_entity {
241 EOUniquerRecord *rec;
243 if (_key == nil || _entity == nil)
246 if (![_key isKindOfClass:[EOPrimaryKeyDictionary class]]) {
247 [NSException raise:NSInvalidArgumentException
249 @"attempt to record object with non "
250 @" EOPrimaryKeyDictionary class in EOObjectUniquer."
251 @"This is a bug in EODatabase/Context/Channel."];
254 keyRecord->pkey = _key;
255 keyRecord->entity = _entity;
257 rec = (EOUniquerRecord*)NSMapGet(primaryKeyToRec, keyRecord);
259 return rec ? rec->object : nil;
262 - (EOUniquerRecord *)recordForObject:(id)_object {
263 return (_object == nil)
264 ? (EOUniquerRecord *)NULL
265 : (EOUniquerRecord *)NSMapGet(self->objectsToRec, _object);
268 - (void)recordObject:(id)_object
269 primaryKey:(NSDictionary *)_key
270 entity:(EOEntity *)_entity
271 snapshot:(NSDictionary *)_snapshot
273 EOUniquerRecord *rec = NULL;
274 EOUniquerRecord *orc = NULL;
279 if ((_key == nil) || (_entity == nil)) {
284 if ((_key == nil) && (_snapshot == nil))
287 if (_key && ![_key isKindOfClass:[EOPrimaryKeyDictionary class]]) {
288 [NSException raise:NSInvalidArgumentException
290 @"attempt to record object with non "
291 @" EOPrimaryKeyDictionary class in EOObjectUniquer."
292 @"This is a bug in EODatabase/Context/Channel."];
295 keyRecord->pkey = _key;
296 keyRecord->entity = _entity;
298 rec = (EOUniquerRecord*)NSMapGet(objectsToRec, _object);
300 if (_key && uniquerCompare(NULL, rec, keyRecord)) {
301 ASSIGN(rec->snapshot, _snapshot);
305 orc = (EOUniquerRecord*)NSMapGet(primaryKeyToRec, keyRecord);
306 if (orc && orc != rec) {
308 NSMapRemove(primaryKeyToRec, orc);
310 NSMapRemove(objectsToRec, orc->object);
312 NSMapRemove(primaryKeyToRec, rec);
314 ASSIGN(rec->pkey, _key);
315 ASSIGN(rec->entity, _entity);
316 ASSIGN(rec->snapshot, _snapshot);
319 NSMapInsertKnownAbsent(primaryKeyToRec, rec, rec);
324 rec = (EOUniquerRecord*)NSMapGet(primaryKeyToRec, keyRecord);
326 if (rec->object == _object) {
327 ASSIGN(rec->snapshot, _snapshot);
331 NSMapRemove(objectsToRec, rec->object);
333 ASSIGN(rec->snapshot, _snapshot);
334 rec->object = _object;
335 NSMapInsertKnownAbsent(objectsToRec, _object, rec);
339 rec = uniquerCreate(_key, _entity, _object, _snapshot);
341 NSMapInsertKnownAbsent(primaryKeyToRec, rec, rec);
342 NSMapInsertKnownAbsent(objectsToRec, _object, rec);
346 /* This method is called by the Boehm's garbage collector when an object
348 - (void)_objectWillFinalize:(id)_object {
349 // printf ("_objectWillFinalize: %p (%s)\n",
350 // _object, class_get_class_name ([_object class]));
351 [self forgetObject:_object];
354 + (void)forgetObject:(id)_object {
359 for (i = [uniquerExtent count] - 1; i >= 0; i--) {
360 EOObjectUniquer *uniquer;
362 uniquer = [[uniquerExtent objectAtIndex:i] nonretainedObjectValue];
363 [uniquer forgetObject:_object];
369 @end /* EOObjectUniquer */