DPQueue is now a two-lock concurrent queue.
[hom.git] / Source / DPCollectionEnumeration.m
blobb11849fd3b5ad654258cf39a733cef221362a6c3
1 //
2 //  DPCollectionEnumeration.m
3 //  HigherOrderMessaging
4 //
5 //  Created by Ofri Wolfus on 11/05/07.
6 //  Copyright 2007 Ofri Wolfus. All rights reserved.
7 //
8 //  Redistribution and use in source and binary forms, with or without modification,
9 //  are permitted provided that the following conditions are met:
10 //  
11 //  1. Redistributions of source code must retain the above copyright
12 //  notice, this list of conditions and the following disclaimer.
13 //  2. Redistributions in binary form must reproduce the above copyright
14 //  notice, this list of conditions and the following disclaimer in the
15 //  documentation and/or other materials provided with the distribution.
16 //  3. Neither the name of Ofri Wolfus nor the names of his contributors
17 //  may be used to endorse or promote products derived from this software
18 //  without specific prior written permission.
20 //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 //  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 //  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 //  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 //  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 //  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 //  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 //  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 //  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #import "DPCollectionEnumeration.h"
32 #import "DPMessage.h"
33 #import "DPObjCRuntime.h"
34 #include <stdarg.h>
35 #include <alloca.h>
36 #include <libkern/OSAtomic.h>
39 @interface NSObject (DPIntrospectionAdditions)
42  * NOTE: Selectors of variable arguments methods must also be
43  * added to -[_DPUninitializedMessage forward::] or the arguments
44  * will get cut off in the message instance.
45  */
48  * The following two methods allow us to customize at runtime
49  * the types description used with a given message/selector.
50  * NSObject's implementation simply returns the result of
51  * class_getInstance(/Class)Method(), but NSString overrides
52  * them in order to support its format string messages.
53  * Currently NSString is the only class that has a custom
54  * implementation for these.
55  */
56 - (const char *)typeEncodingForSelector:(SEL)sel;
57 - (const char *)typeEncodingForMessage:(DPMessage *)msg;
59 @end
62 @interface DPIdEnumerator : DPEnumerator {
65 - (id)initWithObjects:(id *)obj count:(unsigned)c freeWhenDone:(BOOL)flag;
66 @end
68 @implementation DPIdEnumerator
70 typedef struct {
71         id *objects;
72         unsigned count, index;
73         BOOL freeWhenDone;
74 } DPIDEnumContent;
76 - (id)initWithObjects:(id *)obj count:(unsigned)c freeWhenDone:(BOOL)flag {
77         if ((self = [super init])) {
78                 DPIDEnumContent *content = calloc(1, sizeof(DPIDEnumContent));
79                 _reserved = content;
80                 content->objects = obj;
81                 content->count = c;
82                 content->index = 0U;
83                 content->freeWhenDone = flag;
84         }
85         
86         return self;
89 - (void)dealloc {
90         if (((DPIDEnumContent *)_reserved)->freeWhenDone)
91                 free(((DPIDEnumContent *)_reserved)->objects);
92         free(_reserved);
93         [super dealloc];
96 - (id)nextObject {
97         id o = nil;
98         DPIDEnumContent *content = _reserved;
99         
100         if (content->index < content->count) {
101                 o = content->objects[content->index];
102                 content->index++;
103         }
104         
105         return o;
108 - (BOOL)isValid {
109         return ((DPIDEnumContent *)_reserved)->index < ((DPIDEnumContent *)_reserved)->count;
112 - (unsigned)count {
113         return ((DPIDEnumContent *)_reserved)->count;
116 - (void)reset {
117         ((DPIDEnumContent *)_reserved)->index = 0U;
120 @end
123 @interface DPArrayEnumerator : DPEnumerator {
126 - (id)initWithArray:(NSArray *)arr;
127 @end
129 @implementation DPArrayEnumerator
131 typedef struct {
132         NSArray *arr;
133         unsigned count, index;
134         IMP objectAtIndexImp;
135 } DPArrayEnumeratorContent;
137 - (id)initWithArray:(NSArray *)a {
138         if ((self = [super init])) {
139                 DPArrayEnumeratorContent *content = calloc(1, sizeof(DPArrayEnumeratorContent));
140                 _reserved = content;
141                 content->arr = [a retain];
142                 content->count = [a count];
143                 content->index = 0U;
144                 content->objectAtIndexImp = [a methodForSelector:@selector(objectAtIndex:)];
145         }
146         
147         return self;
150 - (void)dealloc {
151         [((DPArrayEnumeratorContent *)_reserved)->arr release];
152         free(_reserved);
153         [super dealloc];
156 - (id)nextObject {
157         id o = nil;
158         DPArrayEnumeratorContent *content = _reserved;
159         
160         if (content->index < content->count) {
161                 o = content->objectAtIndexImp(content->arr, @selector(objectAtIndex:), content->index);
162                 ++(content->index);
163         }
164         
165         return o;
168 - (BOOL)isValid {
169         return ((DPArrayEnumeratorContent *)_reserved)->index < ((DPArrayEnumeratorContent *)_reserved)->count;
172 - (unsigned)count {
173         return ((DPArrayEnumeratorContent *)_reserved)->count;
176 - (void)reset {
177         ((DPArrayEnumeratorContent *)_reserved)->index = 0U;
180 @end
182 @interface DPCollectionEnumerator : DPEnumerator {
185 @end
187 @implementation DPCollectionEnumerator
189 typedef struct {
190         id <DPEnumeration> collection;
191         DPEnumerationState state;
192         id buff[16];
193         unsigned count, index;
194 } DPCollectionEnumeratorContent;
196 - (id)initWithCollection:(id <DPEnumeration>)c {
197         if ((self = [super init])) {
198                 DPCollectionEnumeratorContent *content = calloc(1, sizeof(DPCollectionEnumeratorContent));
199                 _reserved = content;
200                 content->collection = [(id)c retain];
201                 content->state.state = 0;
202                 content->state.items = content->buff;
203                 content->state.info = NULL;
204                 content->state.release = NULL;
205                 content->index = 0;
206                 content->count = [content->collection enumerateWithState:&(content->state)
207                                                                                                                  objects:content->buff
208                                                                                                                    count:16];
209         }
210         
211         return self;
214 - (void)dealloc {
215         DPCollectionEnumeratorContent *content = _reserved;
216         if (content->state.release)
217                 content->state.release(&(content->state));
218         
219         [(id)(content->collection) release];
220         free(_reserved);
221         [super dealloc];
224 - (BOOL)isValid {
225         return ((DPCollectionEnumeratorContent *)_reserved)->count > 0;
228 - (unsigned)count {
229         DPCollectionEnumeratorContent *content = _reserved;
230         
231         if ([(id)(content->collection) respondsToSelector:@selector(count)])
232                 return [(id)(content->collection) count];
233         else
234                 return content->count;
237 - (void)reset {
238         DPCollectionEnumeratorContent *content = _reserved;
239         
240         if (content->state.release)
241                 content->state.release(&(content->state));
242         content->state.state = 0;
243         content->state.items = content->buff;
244         content->state.info = NULL;
245         content->state.release = NULL;
246         content->index = 0;
247         content->count = [content->collection enumerateWithState:&(content->state)
248                                                                                                          objects:content->buff
249                                                                                                            count:16];
252 - (id)nextObject {
253         DPCollectionEnumeratorContent *content = _reserved;
254         
255         if (content->count == 0) {
256                 if (content->state.release) {
257                         content->state.release(&(content->state));
258                         content->state.release = NULL;
259                 }
260                 
261                 return nil;
262         }
263         
264         if (content->index < content->count)
265                 return content->state.items[content->index++];
266         else {
267                 content->index = 0;
268                 content->count = [content->collection enumerateWithState:&(content->state)
269                                                                                                                  objects:content->buff
270                                                                                                                    count:16];
271                 return [self nextObject];
272         }
275 @end
278 @implementation DPEnumerator
280 #if defined(__LP64__) || defined(__ppc64__) || defined(__i386__) || defined(__x86_64__)
281 #define AtomicCompareAndSwapBarrier OSAtomicCompareAndSwap64Barrier
282 #else
283 #define AtomicCompareAndSwapBarrier OSAtomicCompareAndSwap32Barrier
284 #endif
286 // Nobody should ever need more than 100 enumerators at once!
287 // If you do, you're doing something horribly horribly wrong.
288 #define DP_MAX_ENUMERATORS      100
289 // This is a block of DP_MAX_ENUMERATORS DPEnumerator instances,
290 // allocated one after the other. Every instance is actually
291 // struct { Class isa; void *_reserved; }.
292 // Zeroed isa members are freed instances that can be reused.
293 static DPEnumerator *_DPEnumeratorsPool = NULL;
297 + (void)load {
298         // Before we do anything, we must allocate a pool of instances.
299         _DPEnumeratorsPool = calloc(DP_MAX_ENUMERATORS, class_getInstanceSize(self));
302 // Override the default allocation method
303 // and return an instance from our pool.
304 + (id)allocWithZone:(NSZone *)zone {
305         unsigned i;
306         DPEnumerator *enumerator = NULL;
307         
308         for (i = 0; i < DP_MAX_ENUMERATORS; i++) {
309                 enumerator = _DPEnumeratorsPool + i;
310                 if (AtomicCompareAndSwapBarrier(0, (intptr_t)self,
311                                                                                 (void *)enumerator /* we cast to void here to avoid the warning */))
312                 {
313                         break;
314                 }
315         }
316         
317         // Make sure we got an enumerator and not ran out of space
318         NSAssert(__builtin_expect(i < DP_MAX_ENUMERATORS, 1), @"Dude, you're using waay too much enumerators at once!");
319         
320         return enumerator;
323 // We never really free enumerators. We just reuse "released" ones.
324 - (void)dealloc {
325         // Mark ourself as free
326         self->isa = NULL;
327         // Stop GCC from whining
328         return;
329         [super dealloc];
332 + (id)enumeratorWithObjects:(id *)objects
333                                           count:(unsigned)count
334                            freeWhenDone:(BOOL)flag
336         return [[[DPIdEnumerator alloc] initWithObjects:objects
337                                                                                           count:count
338                                                                            freeWhenDone:flag] autorelease];
341 + (id)enumeratorForArray:(NSArray *)arr {
342         return [[[DPArrayEnumerator alloc] initWithArray:arr] autorelease];
345 + (id)enumeratorForCollection:(id <DPEnumeration>)collection {
346         return [[[DPCollectionEnumerator alloc] initWithCollection:collection] autorelease];
349 - (id)nextObject {
350         return nil;
353 - (NSArray *)allObjects {
354         NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[self count]];
355         id obj;
356         
357         while ((obj = [self nextObject]))
358                 [arr addObject:obj];
359         
360         return obj;
363 - (BOOL)isValid {
364         return NO;
367 - (unsigned)count {
368         return 0U;
371 - (void)reset {
374 - (id)forward:(SEL)sel :(marg_list)args {
375         id o;
376         unsigned frameSize = dp_maxArgSizeForSelector(sel);
377         
378         while ((o = [self nextObject]))
379                 objc_msgSendv(o, sel, frameSize, args);
380         
381         return self;
384 BOOL DPIsEnumeratedArgument(void *ptr) {
385         return (DPEnumerator *)ptr >= _DPEnumeratorsPool &&
386                         (DPEnumerator *)ptr <= _DPEnumeratorsPool + DP_MAX_ENUMERATORS;
389 @end
391 #define _DP_ENUM_OBJ 'z'
393 DP_STATIC_INLINE char *DPCopyArgumentTypesMap(const char *typedesc,
394                                                                                           marg_list argsFrame,
395                                                                                           Class enumeratorClass)
397         unsigned i, argumentsCount = dp_getNumberOfArguments(typedesc);
398         char *types = calloc(argumentsCount, sizeof(char));
399         
400         for (i = 0U; i < argumentsCount; i++) {
401                 const char *t;
402                 int offset;
403                 
404                 // Get the argument's type
405                 dp_getArgumentInfo(typedesc, i, &t, &offset);
406                 // Remember it
407                 types[i] = *t;
408                 // Check object values against our enumerator type
409                 // and mark the matches
410                 if (types[i] == _C_ID) {
411                         // Get the object from our arguments frame
412                         id obj = dp_margGetObject(argsFrame, offset, id);
413                         // If the object is an instance AND it's class is enumeratorClass or a subclass of it,
414                         // it's an enumerated argument.
415                         if (object_isInstance(obj))
416                                 if (class_isSubclassOfClass(object_getClass(obj), enumeratorClass))
417                                         types[i] = _DP_ENUM_OBJ;
418                 }
419         }
420         
421         return types;
424 DP_STATIC_INLINE int *DPCopyOffsetsOfArguments(const char *typedesc) {
425         unsigned i, argumentsCount = dp_getNumberOfArguments(typedesc);
426         int *offsets = calloc(argumentsCount, sizeof(int));
427         const char *t;
428         
429         for (i = 0U; i < argumentsCount; i++)
430                 dp_getArgumentInfo(typedesc, i, &t, &(offsets[i]));
431         
432         return offsets;
435 DP_STATIC_INLINE void DPUpdateEnumereratedArguments(marg_list origFrame,
436                                                                                                         marg_list newFrame,
437                                                                                                         const char *types,
438                                                                                                         int *offsets,
439                                                                                                         unsigned count,
440                                                                                                         BOOL allowNil)
442         unsigned i;
443         for (i = 2U; i < count; i++) {
444                 if (types[i] == _DP_ENUM_OBJ) {
445                         // Get the enumerator from the original frame,
446                         // send it a -nextObject message, and that's our new value.
447                         id val = [dp_margGetObject(origFrame, offsets[i], id) nextObject];
448                         
449                         // If our value != nil than we simply apply it.
450                         // Otherwise, only if nil is a valid value (indicated by allowNil == YES)
451                         // we apply it. This means that if allowNil == NO and an enumerated
452                         // argument ends before time (i.e. it has less objects than the collection)
453                         // the last value returned from it will be used repeatedly.
454                         if (allowNil || val)
455                                 // Set the new value
456                                 dp_margSetObject(newFrame, offsets[i], id, val);
457                 }
458         }
461 DP_STATIC_INLINE BOOL DPHasEnumeratedArguments(const char *types,
462                                                                                            unsigned count)
464         unsigned i;
465         for (i = 2U; i < count; i++)
466                 if (types[i] == _DP_ENUM_OBJ)
467                         return YES;
468         return NO;
470                                                          
472 typedef unsigned (*DPUIMP)(id, SEL, ...);
474 // A generic, collection independent, implementation for -collect:
475 void DPCollectObjects(id <DPEnumeration> collection,
476                                           id resultsCollection,
477                                           DPMessage *message,
478                                           DPEnumerationAppendResultsCallBack callback)
480         // A buffer to which our collection copies objects
481         id buff[16];
482         // Our enumeration state.
483         // TODO: Consolidate it with 10.5's enumeration state?
484         DPEnumerationState state = { 0, buff, NULL, NULL };
485         // Cache the selector, frame length and the frame to save some useless messaging
486         SEL sel = [message selector];
487         unsigned frameLength = [message sizeOfArguments];
488         marg_list origFrame = [message arguments];
489         // A copy of the frame. It's what we use if there are enumerated arguments (DPEnumerator)
490         // in the original frame. This is to avoid modifying the frame of our message.
491         marg_list frame = origFrame;
492         // IMP cacheing is good for large loops
493         SEL enumerateWithStateSel = @selector(enumerateWithState:objects:count:);
494         DPUIMP enumerateWithStateImp = (DPUIMP)[(NSObject *)collection methodForSelector:enumerateWithStateSel];
495         // Get the first 16 objects from our collection
496         // This is the same as [collection enumerateWithState:&state objects:buff count:16]
497         unsigned count = enumerateWithStateImp(collection, enumerateWithStateSel, &state, buff, 16);
498         // An array of chars representing the types of our method's arguments
499         char *types = NULL;
500         // The offset of each argument in our marg_list
501         int *offsets = NULL;
502         // Total number of arguments which is also the size of the two arrays above
503         unsigned argsCount = 0U;
504         
505         // Give up now if our collection is empty
506         if (count == 0U)
507                 goto cleanup;
508         
509         // Get the type encoding we expect from the first object we have.
510         // XXX What to do if the object can't respond to this method and returns NULL?
511         // Sooner ot later this will cause us to crash and/or throw an exception.
512         const char *typeEncoding = [state.items[0] typeEncodingForMessage:message];
513         argsCount = dp_getNumberOfArguments(typeEncoding);
514         
515         if (argsCount > 2) {
516                 // Get a map of our arguments' types
517                 types = DPCopyArgumentTypesMap(typeEncoding, origFrame, [DPEnumerator class]);
518                 
519                 if (DPHasEnumeratedArguments(types, argsCount)) {
520                         unsigned int len = [message realFrameSize];
521                         
522                         // Copy our arguments frame
523                         frame = dp_marg_malloc(len);
524                         memcpy(frame, origFrame, len);
525                         
526                         // Get offsets as well
527                         offsets = DPCopyOffsetsOfArguments(typeEncoding);
528                 }
529         }
530         
531         // Grab all objects from our collection, message them, and store the results
532         while (count > 0U) {
533                 unsigned i;
534                 id results[16];
535                 unsigned resultsCount = 0;
536                 
537                 // Grab the results of all objects
538                 for (i = 0U; i < count; i++) {
539                         // Update any enumerated arguments if needed.
540                         // If any enumerated arguments contain less objects than our collection has
541                         // nil values will be used. This might be useful if the objects in the collection expect
542                         // nil, but otherwise it'll probably cause in assertion failure in the object's
543                         // implementation (which is not really our fault).
544                         // For example, if arr1 contains 5 strings and arr2 contains 3 strings,
545                         // [arr1 collect:MSG(stringByAppendingString:[arr2 each])] will throw an exception
546                         // when trying to append nil to strings 4 and 5.
547                         // NOTE: The last argument to DPUpdateEnumereratedArguments() indicates whether to use
548                         // nil when an enumerated argument is empty or to use the last object repeatedly.
549                         if (offsets != NULL)
550                                 DPUpdateEnumereratedArguments(origFrame, frame, types, offsets, argsCount, YES);
551                         
552                         // Send the message and store its result
553                         results[resultsCount] = objc_msgSendv(state.items[i], sel, frameLength, frame);
554                         ++resultsCount;
555                         
556                         // If our results buffer gets full, empty it and append the results
557                         if (resultsCount == 16) {
558                                 // Append the results to the results collection
559                                 callback(resultsCollection, &state, results, resultsCount);
560                                 // Reset our counter
561                                 resultsCount = 0;
562                         }
563                 }
564                 
565                 // Append the results to the results collection
566                 if (resultsCount)
567                         callback(resultsCollection, &state, results, resultsCount);
568                 
569                 // Get the next 16 objects from our collection
570                 count = enumerateWithStateImp(collection, enumerateWithStateSel, &state, buff, 16);
571         }
572         
573         // Clean up
574         if (argsCount > 2U) {
575                 free(types);
576                 
577                 if (offsets) {
578                         free(offsets);
579                         free(frame);
580                 }
581         }
582 cleanup:
583         // Finally, invoke the release callback and return
584         if (state.release != NULL)
585                 state.release(&state);
588 typedef BOOL (*DPBOOLMSGV)(id, SEL, unsigned arg_size, marg_list arg_frame);
590 // A generic, collection independent, implementation for -selectWhere:
591 // and -rejectWhere:
592 void DPSelectObjects(id <DPEnumeration> collection,
593                                          id resultsCollection,
594                                          DPMessage **messages,
595                                          unsigned messageCount,
596                                          BOOL returnValue,
597                                          DPEnumerationAppendResultsCallBack callback)
599         // A buffer to which our collection copies objects
600         id buff[16];
601         // Our enumeration state.
602         // TODO: Consolidate it with 10.5's enumeration state?
603         DPEnumerationState state = { 0, buff, NULL, NULL };
604         // Cache the selector, frame length and the frame to save some useless messaging
605         SEL sel[messageCount];
606         unsigned frameLength[messageCount];
607         marg_list origFrame[messageCount];
608         // A copy of the frame. It's what we use if there are enumerated arguments (DPEnumerator)
609         // in the original frame. This is to avoid modifying the frame of our message.
610         marg_list frame[messageCount];
611         // IMP cacheing is good for large loops
612         SEL enumerateWithStateSel = @selector(enumerateWithState:objects:count:);
613         DPUIMP enumerateWithStateImp = (DPUIMP)[(NSObject *)collection methodForSelector:enumerateWithStateSel];
614         // Get the first 16 objects from our collection
615         // This is the same as [collection enumerateWithState:&state objects:buff count:16]
616         unsigned count = enumerateWithStateImp(collection, enumerateWithStateSel, &state, buff, 16);
617         // An array of chars representing the types of our method's arguments
618         char *types[messageCount];
619         // The offset of each argument in our marg_list
620         int *offsets[messageCount];
621         // Total number of arguments which is also the size of the two arrays above
622         unsigned argsCount[messageCount];
623         
624         // Just an ordinary index variable
625         unsigned i;
626         
627         // Give up now if our collection is empty
628         if (count == 0U)
629                 goto cleanup;
630         
631         BOOL initialized = NO;
632         
633         // Grab all objects from our collection, message them, and store the results
634         while (count > 0U) {
635                 id results[16];
636                 unsigned resultsCount = 0;
637                 i = 0;
638                 
639                 // Initialize everything if needed
640                 if (__builtin_expect(!initialized, 0)) {
641                         id obj = state.items[0];
642                         
643                         // We don't have the privilege DPCollectObjects() has of working with a single
644                         // message, so we must initialize everything in a loop. Fun.
645                         // Everything here assumes the first object is a model to all others.
646                         for (i = 0; i < messageCount; i++) {
647                                 // Cache all selectors, frame lengths and argument lists
648                                 sel[i] = [messages[i] selector];
649                                 frameLength[i] = [messages[i] sizeOfArguments];
650                                 origFrame[i] = [messages[i] arguments];
651                                 
652                                 // Get the type encoding we expect from the first object we have.
653                                 // XXX: What to do if the object can't respond to this method and returns NULL?
654                                 // Sooner ot later this will cause us to crash and/or throw an exception.
655                                 const char *typeEncoding = [obj typeEncodingForMessage:messages[i]];
656                                 argsCount[i] = dp_getNumberOfArguments(typeEncoding);
657                                 
658                                 // NULL offsets means no enumerated arguments
659                                 offsets[i] = NULL;
660                                 // Our frame is the original one
661                                 frame[i] = origFrame[i];
662                                 
663                                 if (argsCount[i] > 2) {
664                                         // Get a map of our arguments' types
665                                         types[i] = DPCopyArgumentTypesMap(typeEncoding, origFrame[i], [DPEnumerator class]);
666                                         
667                                         if (DPHasEnumeratedArguments(types[i], argsCount[i])) {
668                                                 unsigned int len = [messages[i] realFrameSize];
669                                                 
670                                                 // Copy our arguments frame
671                                                 frame[i] = dp_marg_malloc(len);
672                                                 memcpy(frame[i], origFrame[i], len);
673                                                 
674                                                 // Get offsets as well
675                                                 offsets[i] = DPCopyOffsetsOfArguments(typeEncoding);
676                                         }
677                                 }
678                                 
679                                 // Update any enumerated arguments
680                                 if (offsets[i] != NULL)
681                                         DPUpdateEnumereratedArguments(origFrame[i], frame[i], types[i], offsets[i], argsCount[i], YES);
682                                 
683                                 // The last message should return BOOL.
684                                 // If it's YES, we add the original object to our results array.
685                                 if (__builtin_expect(i == messageCount - 1, 0)) {
686                                         if (((DPBOOLMSGV)(objc_msgSendv))(obj, sel[i],
687                                                                                                           frameLength[i],
688                                                                                                           frame[i]) == returnValue)
689                                         {
690                                                 results[resultsCount] = state.items[0];
691                                                 ++resultsCount;
692                                         }
693                                 } else {
694                                         // Send the message to our object and use the result
695                                         obj = objc_msgSendv(obj, sel[i], frameLength[i], frame[i]);
696                                 }
697                         }
698                         
699                         // We're good to go
700                         initialized = YES;
701                         // Now start the loop from the second object
702                         i = 1;
703                 }
704                 
705                 // Grab the results of all objects
706                 for (/* i is initialized above */; i < count; i++) {
707                         unsigned j = 0;
708                         id obj = state.items[i];
709                         
710                         for (j = 0; j < messageCount - 1; j++) {
711                                 // Update any enumerated arguments if needed.
712                                 // If any enumerated arguments contain less objects than our collection has
713                                 // nil values will be used. This might be useful if the objects in the collection expect
714                                 // nil, but otherwise it'll probably cause in assertion failure in the object's
715                                 // implementation (which is not really our fault).
716                                 // For example, if arr1 contains 5 strings and arr2 contains 3 strings,
717                                 // [arr1 collect:MSG(stringByAppendingString:[arr2 each])] will throw an exception
718                                 // when trying to append nil to strings 4 and 5.
719                                 // NOTE: The last argument to DPUpdateEnumereratedArguments() indicates whether to use
720                                 // nil when an enumerated argument is empty or to use the last object repeatedly.
721                                 if (offsets[j] != NULL)
722                                         DPUpdateEnumereratedArguments(origFrame[j], frame[j], types[j],
723                                                                                                   offsets[j], argsCount[j], YES);
724                         
725                                 // Send the message and store its result
726                                 obj = objc_msgSendv(obj, sel[j], frameLength[j], frame[j]);
727                         }
728                         
729                         if (offsets[j] != NULL)
730                                 DPUpdateEnumereratedArguments(origFrame[j], frame[j], types[j],
731                                                                                           offsets[j], argsCount[j], YES);
732                         
733                         // The last message should return BOOL.
734                         // If it's YES, we add the original object to our results array.
735                         if (((DPBOOLMSGV)(objc_msgSendv))(obj, sel[j],
736                                                                                           frameLength[j],
737                                                                                           frame[j]) == returnValue)
738                         {
739                                 results[resultsCount] = state.items[i];
740                                 ++resultsCount;
741                                 
742                                 if (resultsCount == 16) {
743                                         // Append the results to the results collection
744                                         callback(resultsCollection, &state, results, resultsCount);
745                                         // Reset our counter
746                                         resultsCount = 0;
747                                 }
748                         }
749                 }
750                 
751                 // Append the results to the results collection
752                 if (resultsCount)
753                         callback(resultsCollection, &state, results, resultsCount);
754                 
755                 // Get the next 16 objects from our collection
756                 count = enumerateWithStateImp(collection, enumerateWithStateSel, &state, buff, 16);
757         }
758         
759         // Clean up
760         for (i = 0; i < messageCount; i++) {
761                 if (argsCount[i] > 2U) {
762                         free(types[i]);
763                         
764                         if (offsets[i]) {
765                                 free(offsets[i]);
766                                 free(frame[i]);
767                         }
768                 }
769         }
770 cleanup:
771         // Finally, invoke the release callback and return
772                 if (state.release != NULL)
773                         state.release(&state);
776 id DPFindObject(id <DPEnumeration> collection,
777                                   DPMessage **messages,
778                                   unsigned messageCount,
779                                   BOOL returnValue)
781         // A buffer to which our collection copies objects
782         id buff[16];
783         // Our enumeration state.
784         // TODO: Consolidate it with 10.5's enumeration state?
785         DPEnumerationState state = { 0, buff, NULL, NULL };
786         // Cache the selector, frame length and the frame to save some useless messaging
787         SEL sel[messageCount];
788         unsigned frameLength[messageCount];
789         marg_list origFrame[messageCount];
790         // A copy of the frame. It's what we use if there are enumerated arguments (DPEnumerator)
791         // in the original frame. This is to avoid modifying the frame of our message.
792         marg_list frame[messageCount];
793         // IMP cacheing is good for large loops
794         SEL enumerateWithStateSel = @selector(enumerateWithState:objects:count:);
795         DPUIMP enumerateWithStateImp = (DPUIMP)[(NSObject *)collection methodForSelector:enumerateWithStateSel];
796         // Get the first 16 objects from our collection
797         // This is the same as [collection enumerateWithState:&state objects:buff count:16]
798         unsigned count = enumerateWithStateImp(collection, enumerateWithStateSel, &state, buff, 16);
799         // An array of chars representing the types of our method's arguments
800         char *types[messageCount];
801         // The offset of each argument in our marg_list
802         int *offsets[messageCount];
803         // Total number of arguments which is also the size of the two arrays above
804         unsigned argsCount[messageCount];
805         // Our return value
806         id result = nil;
807         
808         // Just an ordinary index variable
809         unsigned i;
810         
811         // Give up now if our collection is empty
812         if (count == 0U)
813                 goto cleanup;
814         
815         BOOL initialized = NO;
816         
817         // Grab all objects from our collection, message them, and store the results
818         while (count > 0U) {
819                 i = 0;
820                 
821                 // Initialize everything if needed
822                 if (__builtin_expect(!initialized, 0)) {
823                         id obj = state.items[0];
824                         
825                         // We don't have the privilege DPCollectObjects() has of working with a single
826                         // message, so we must initialize everything in a loop. Fun.
827                         // Everything here assumes the first object is a model to all others.
828                         for (i = 0; i < messageCount; i++) {
829                                 // Cache all selectors, frame lengths and argument lists
830                                 sel[i] = [messages[i] selector];
831                                 frameLength[i] = [messages[i] sizeOfArguments];
832                                 origFrame[i] = [messages[i] arguments];
833                                 
834                                 // Get the type encoding we expect from the first object we have.
835                                 // XXX: What to do if the object can't respond to this method and returns NULL?
836                                 // Sooner ot later this will cause us to crash and/or throw an exception.
837                                 const char *typeEncoding = [obj typeEncodingForMessage:messages[i]];
838                                 argsCount[i] = dp_getNumberOfArguments(typeEncoding);
839                                 
840                                 // NULL offsets means no enumerated arguments
841                                 offsets[i] = NULL;
842                                 // Our frame is the original one
843                                 frame[i] = origFrame[i];
844                                 
845                                 if (argsCount[i] > 2) {
846                                         // Get a map of our arguments' types
847                                         types[i] = DPCopyArgumentTypesMap(typeEncoding, origFrame[i], [DPEnumerator class]);
848                                         
849                                         if (DPHasEnumeratedArguments(types[i], argsCount[i])) {
850                                                 unsigned int len = [messages[i] realFrameSize];
851                                                 
852                                                 // Copy our arguments frame
853                                                 frame[i] = dp_marg_malloc(len);
854                                                 memcpy(frame[i], origFrame[i], len);
855                                                 
856                                                 // Get offsets as well
857                                                 offsets[i] = DPCopyOffsetsOfArguments(typeEncoding);
858                                         }
859                                 }
860                                 
861                                 // Update any enumerated arguments
862                                 if (offsets[i] != NULL)
863                                         DPUpdateEnumereratedArguments(origFrame[i], frame[i], types[i], offsets[i], argsCount[i], YES);
864                                 
865                                 // Send the message to our object and use the result
866                                 obj = objc_msgSendv(obj, sel[i], frameLength[i], frame[i]);
867                                 
868                                 // The last message should return BOOL.
869                                 // The first to return 'returnValue' is our match.
870                                 if (i == messageCount - 1 && (BOOL)(long)obj == returnValue) {
871                                         result = state.items[0];
872                                         // Go do some final cleanups and return
873                                         goto finish;
874                                 }
875                         }
876                         
877                         // We're good to go
878                         initialized = YES;
879                         // Now start the loop from the second object
880                         i = 1;
881                 }
882                 
883                 // Grab the results of all objects
884                 for (/* i is initialized above */; i < count; i++) {
885                         unsigned j = 0;
886                         id obj = state.items[i];
887                         
888                         for (j = 0; j < messageCount - 1; j++) {
889                                 // Update any enumerated arguments if needed.
890                                 // If any enumerated arguments contain less objects than our collection has
891                                 // nil values will be used. This might be useful if the objects in the collection expect
892                                 // nil, but otherwise it'll probably cause in assertion failure in the object's
893                                 // implementation (which is not really our fault).
894                                 // For example, if arr1 contains 5 strings and arr2 contains 3 strings,
895                                 // [arr1 collect:MSG(stringByAppendingString:[arr2 each])] will throw an exception
896                                 // when trying to append nil to strings 4 and 5.
897                                 // NOTE: The last argument to DPUpdateEnumereratedArguments() indicates whether to use
898                                 // nil when an enumerated argument is empty or to use the last object repeatedly.
899                                 if (offsets[j] != NULL)
900                                         DPUpdateEnumereratedArguments(origFrame[j], frame[j], types[j],
901                                                                                                   offsets[j], argsCount[j], YES);
902                                 
903                                 // Send the message and store its result
904                                 obj = objc_msgSendv(obj, sel[j], frameLength[j], frame[j]);
905                         }
906                         
907                         if (offsets[j] != NULL)
908                                 DPUpdateEnumereratedArguments(origFrame[j], frame[j], types[j],
909                                                                                           offsets[j], argsCount[j], YES);
910                         
911                         // The last message should return BOOL.
912                         // The first object to return 'returnValue' is our match.
913                         if (((DPBOOLMSGV)(objc_msgSendv))(obj, sel[j],
914                                                                                           frameLength[j],
915                                                                                           frame[j]) == returnValue)
916                         {
917                                 result = state.items[i];
918                                 // Go do some final cleanups and return
919                                 goto finish;
920                         }
921                 }
922                 
923                 // Get the next 16 objects from our collection
924                 count = enumerateWithStateImp(collection, enumerateWithStateSel, &state, buff, 16);
925         }
926         
927 finish:
928         // Clean up
929         for (i = 0; i < messageCount; i++) {
930                 if (argsCount[i] > 2U) {
931                         free(types[i]);
932                         
933                         if (offsets[i]) {
934                                 free(offsets[i]);
935                                 free(frame[i]);
936                         }
937                 }
938         }
939 cleanup:
940         // Finally, invoke the release callback and return
941                 if (state.release != NULL)
942                         state.release(&state);
943         
944         return result;
948 @implementation NSArray (DPCollectionEnumeration)
950 static void DPNSArrayAppendResults (id resultsCollection,
951                                                                         DPEnumerationState *state,
952                                                                         id *objects,
953                                                                         unsigned count)
955         unsigned i;
956         for (i = 0U; i < count; i++)
957                 CFArrayAppendValue((CFMutableArrayRef)resultsCollection, objects[i]);
960 - (unsigned)enumerateWithState:(DPEnumerationState *)state
961                                            objects:(id *)buff
962                                                  count:(unsigned)buffLen
964         CFIndex count = CFArrayGetCount((CFArrayRef)self);
965         
966         if (state->state < count) {
967                 CFIndex len = (state->state + buffLen < count) ? buffLen : (count - state->state);
968                 
969                 CFArrayGetValues((CFArrayRef)self, CFRangeMake(state->state, len), (const void **)buff);
970                 state->state += len;
971                 return len;
972         }
973         
974         return 0;
978 - (id)each {
979         //return [DPEnumerator enumeratorForArray:self];
980         return [DPEnumerator enumeratorForCollection:self];
982         
984 - (id)collect:(DPMessage *)argumentMessage {
985         NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[self count]];
986         
987         DPCollectObjects(self, arr, argumentMessage, DPNSArrayAppendResults);
988         return arr;
991 - (id)pick:(BOOL)val where:(DPMessage *)firstMsg additionalMessages:(va_list)msgs {
992         va_list va;
993         unsigned i, count = 1;
994         NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count] / 2];
995         
996         va_copy(va, msgs);
997         while (va_arg(va, id))
998                 ++count;
999         va_end(va);
1000         
1001         DPMessage *messages[count];
1002         messages[0] = firstMsg;
1003         
1004         va_copy(va, msgs);
1005         
1006         for (i = 1; i < count; i++) {
1007                 messages[i] = va_arg(va, DPMessage *);
1008         }
1009         
1010         va_end(va);
1011         
1012         DPSelectObjects(self, result, messages, count, val, DPNSArrayAppendResults);
1013         return result;
1016 - (id)selectWhere:(DPMessage *)firstMessage, ... {
1017         va_list va;
1018         id r = nil;
1019         
1020         NSAssert(firstMessage != nil, @"Passed nil message");
1021         
1022         va_start(va, firstMessage);
1023         r = [self pick:YES where:firstMessage additionalMessages:va];
1024         va_end(va);
1025         
1026         return r;
1029 - (id)rejectWhere:(DPMessage *)firstMessage, ... {
1030         va_list va;
1031         id r = nil;
1032         
1033         NSAssert(firstMessage != nil, @"Passed nil message");
1034         
1035         va_start(va, firstMessage);
1036         r = [self pick:NO where:firstMessage additionalMessages:va];
1037         va_end(va);
1038         
1039         return r;
1042 - (id)find:(BOOL)val where:(DPMessage *)firstMsg additionalMessages:(va_list)msgs {
1043         va_list va;
1044         unsigned i, count = 1;
1045         
1046         va_copy(va, msgs);
1047         while (va_arg(va, id))
1048                 ++count;
1049         va_end(va);
1050         
1051         DPMessage *messages[count];
1052         messages[0] = firstMsg;
1053         
1054         va_copy(va, msgs);
1055         
1056         for (i = 1; i < count; i++) {
1057                 messages[i] = va_arg(va, DPMessage *);
1058         }
1059         
1060         va_end(va);
1061         
1062         return DPFindObject(self, messages, count, val);
1065 - (id)findObjectWhere:(DPMessage *)firstMessage, ... {
1066         va_list va;
1067         id r = nil;
1068         
1069         NSAssert(firstMessage != nil, @"Passed nil message");
1070         
1071         va_start(va, firstMessage);
1072         r = [self find:YES where:firstMessage additionalMessages:va];
1073         va_end(va);
1074         
1075         return r;
1078 @end
1081 @implementation NSSet (DPCollectionEnumeration)
1083 static void DPNSSetAppendResults (id resultsCollection,
1084                                                                   DPEnumerationState *state,
1085                                                                   id *objects,
1086                                                                   unsigned count)
1088         unsigned i;
1089         for (i = 0U; i < count; i++)
1090                 CFSetAddValue((CFMutableSetRef)resultsCollection, objects[i]);
1093 static void DPNSSetEnumerationCleanup(DPEnumerationState *state) {
1094         if (state->items)
1095                 free(state->items);
1098 - (unsigned)enumerateWithState:(DPEnumerationState *)state
1099                                            objects:(id *)buff
1100                                                  count:(unsigned)buffLen
1102         CFIndex count = CFSetGetCount((CFSetRef)self);
1103         
1104         // Just copy all objects at once
1105         if (state->state == 0 && count > 0) {
1106                 id *objects = calloc(count, sizeof(id));
1107                 
1108                 CFSetGetValues((CFSetRef)self, (const void **)objects);
1109                 state->items = objects;
1110                 state->state = count - 1;
1111                 state->release = (void *)DPNSSetEnumerationCleanup;
1112                 
1113                 return count;
1114         }
1115         
1116         return 0;
1120 - (id)each {
1121         CFIndex count = CFSetGetCount((CFSetRef)self);
1122         id *objects = calloc(count, sizeof(id));
1123         
1124         CFSetGetValues((CFSetRef)self, (const void **)objects);
1125         return [DPEnumerator enumeratorWithObjects:objects
1126                                                                                  count:count
1127                                                                   freeWhenDone:YES];
1131 - (id)collect:(DPMessage *)argumentMessage {
1132         NSMutableSet *arr = [NSMutableSet setWithCapacity:[self count]];
1133         
1134         DPCollectObjects(self, arr, argumentMessage, DPNSSetAppendResults);
1135         return arr;
1138 - (id)pick:(BOOL)val where:(DPMessage *)firstMsg additionalMessages:(va_list)msgs {
1139         va_list va;
1140         unsigned i, count = 1;
1141         NSMutableSet *result = [NSMutableSet setWithCapacity:[self count] / 2];
1142         
1143         va_copy(va, msgs);
1144         while (va_arg(va, id))
1145                 ++count;
1146         va_end(va);
1147         
1148         DPMessage *messages[count];
1149         messages[0] = firstMsg;
1150         
1151         va_copy(va, msgs);
1152         
1153         for (i = 1; i < count; i++) {
1154                 messages[i] = va_arg(va, DPMessage *);
1155         }
1156         
1157         va_end(va);
1158         
1159         DPSelectObjects(self, result, messages, count, val, DPNSSetAppendResults);
1160         return result;
1163 - (id)selectWhere:(DPMessage *)firstMessage, ... {
1164         va_list va;
1165         id r = nil;
1166         
1167         NSAssert(firstMessage != nil, @"Passed nil message");
1168         
1169         va_start(va, firstMessage);
1170         r = [self pick:YES where:firstMessage additionalMessages:va];
1171         va_end(va);
1172         
1173         return r;
1176 - (id)rejectWhere:(DPMessage *)firstMessage, ... {
1177         va_list va;
1178         id r = nil;
1179         
1180         NSAssert(firstMessage != nil, @"Passed nil message");
1181         
1182         va_start(va, firstMessage);
1183         r = [self pick:NO where:firstMessage additionalMessages:va];
1184         va_end(va);
1185         
1186         return r;
1189 - (id)find:(BOOL)val where:(DPMessage *)firstMsg additionalMessages:(va_list)msgs {
1190         va_list va;
1191         unsigned i, count = 1;
1192         
1193         va_copy(va, msgs);
1194         while (va_arg(va, id))
1195                 ++count;
1196         va_end(va);
1197         
1198         DPMessage *messages[count];
1199         messages[0] = firstMsg;
1200         
1201         va_copy(va, msgs);
1202         
1203         for (i = 1; i < count; i++) {
1204                 messages[i] = va_arg(va, DPMessage *);
1205         }
1206         
1207         va_end(va);
1208         
1209         return DPFindObject(self, messages, count, val);
1212 - (id)findObjectWhere:(DPMessage *)firstMessage, ... {
1213         va_list va;
1214         id r = nil;
1215         
1216         NSAssert(firstMessage != nil, @"Passed nil message");
1217         
1218         va_start(va, firstMessage);
1219         r = [self find:YES where:firstMessage additionalMessages:va];
1220         va_end(va);
1221         
1222         return r;
1225 @end
1227 @implementation NSDictionary (DPCollectionEnumeration)
1229 static void DPNSDictionaryAppendResults (id resultsCollection,
1230                                                                                  DPEnumerationState *state,
1231                                                                                  id *objects,
1232                                                                                  unsigned count)
1234         unsigned i;
1235         id *obj = state->items;
1236         // We hold the keys in the info field
1237         id *key = state->info;
1238         
1239         for (i = 0U; i < count; i++) {
1240                 int j;
1241                 
1242                 // Find the index of the key matching the object we need to apply
1243                 for (j = i; (*obj) != objects[i]; ++j)
1244                         ++obj;
1245                 
1246                 CFDictionaryAddValue((CFMutableDictionaryRef)resultsCollection, key[j], *obj);
1247         }
1250 static void DPNSDictionaryEnumerationCleanup(DPEnumerationState *state) {
1251         if (state->items)
1252                 free(state->items);
1253         
1254         if (state->info)
1255                 free(state->info);
1258 - (unsigned)enumerateWithState:(DPEnumerationState *)state
1259                                            objects:(id *)buff
1260                                                  count:(unsigned)buffLen
1262         CFIndex count = CFDictionaryGetCount((CFDictionaryRef)self);
1263         
1264         // Just copy all objects at once
1265         if (state->state == 0 && count > 0) {
1266                 id *values = calloc(count, sizeof(id));
1267                 id *keys = calloc(count, sizeof(id));
1268                 
1269                 CFDictionaryGetKeysAndValues((CFDictionaryRef)self,
1270                                                                          (const void **)keys,
1271                                                                          (const void **)values);
1272                 state->items = values;
1273                 state->info = keys;
1274                 state->state = count - 1;
1275                 state->release = (void *)DPNSDictionaryEnumerationCleanup;
1276                 
1277                 return count;
1278         }
1279         
1280         return 0;
1284 - (id)each {
1285         CFIndex count = CFSetGetCount((CFSetRef)self);
1286         id *objects = calloc(count, sizeof(id));
1287         
1288         CFDictionaryGetKeysAndValues((CFDictionaryRef)self,
1289                                                                  NULL, (const void **)objects);
1290         return [DPEnumerator enumeratorWithObjects:objects
1291                                                                                  count:count
1292                                                                   freeWhenDone:YES];
1296 - (id)collect:(DPMessage *)argumentMessage {
1297         NSMutableDictionary *arr = [NSMutableDictionary dictionaryWithCapacity:[self count]];
1298         
1299         DPCollectObjects(self, arr, argumentMessage, DPNSDictionaryAppendResults);
1300         return arr;
1303 - (id)pick:(BOOL)val where:(DPMessage *)firstMsg additionalMessages:(va_list)msgs {
1304         va_list va;
1305         unsigned i, count = 1;
1306         NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:[self count] / 2];
1307         
1308         va_copy(va, msgs);
1309         while (va_arg(va, id))
1310                 ++count;
1311         va_end(va);
1312         
1313         DPMessage *messages[count];
1314         messages[0] = firstMsg;
1315         
1316         va_copy(va, msgs);
1317         
1318         for (i = 1; i < count; i++) {
1319                 messages[i] = va_arg(va, DPMessage *);
1320         }
1321         
1322         va_end(va);
1323         
1324         DPSelectObjects(self, result, messages, count, val, DPNSDictionaryAppendResults);
1325         return result;
1328 - (id)selectWhere:(DPMessage *)firstMessage, ... {
1329         va_list va;
1330         id r = nil;
1331         
1332         NSAssert(firstMessage != nil, @"Passed nil message");
1333         
1334         va_start(va, firstMessage);
1335         r = [self pick:YES where:firstMessage additionalMessages:va];
1336         va_end(va);
1337         
1338         return r;
1341 - (id)rejectWhere:(DPMessage *)firstMessage, ... {
1342         va_list va;
1343         id r = nil;
1344         
1345         NSAssert(firstMessage != nil, @"Passed nil message");
1346         
1347         va_start(va, firstMessage);
1348         r = [self pick:NO where:firstMessage additionalMessages:va];
1349         va_end(va);
1350         
1351         return r;
1354 - (id)find:(BOOL)val where:(DPMessage *)firstMsg additionalMessages:(va_list)msgs {
1355         va_list va;
1356         unsigned i, count = 1;
1357         
1358         va_copy(va, msgs);
1359         while (va_arg(va, id))
1360                 ++count;
1361         va_end(va);
1362         
1363         DPMessage *messages[count];
1364         messages[0] = firstMsg;
1365         
1366         va_copy(va, msgs);
1367         
1368         for (i = 1; i < count; i++) {
1369                 messages[i] = va_arg(va, DPMessage *);
1370         }
1371         
1372         va_end(va);
1373         
1374         return DPFindObject(self, messages, count, val);
1377 - (id)findObjectWhere:(DPMessage *)firstMessage, ... {
1378         va_list va;
1379         id r = nil;
1380         
1381         NSAssert(firstMessage != nil, @"Passed nil message");
1382         
1383         va_start(va, firstMessage);
1384         r = [self find:YES where:firstMessage additionalMessages:va];
1385         va_end(va);
1386         
1387         return r;
1390 @end
1393 @interface NSObject (runtimeMethods)
1394 - (id)forward:(SEL)sel :(marg_list)args;
1395 @end
1398 @implementation NSObject (DPHOMGoodies)
1400 - (id)collect:(DPMessage *)msg {
1401         SEL sel = [msg selector];
1402         marg_list origFrame = [msg arguments], frame = origFrame;
1403         unsigned int frameSize = [msg sizeOfArguments];
1404         const char *typedesc = [self typeEncodingForMessage:msg];
1405         
1406         if (!typedesc)
1407                 return nil;
1408         
1409         unsigned argsCount = dp_getNumberOfArguments(typedesc);
1410         char *types = DPCopyArgumentTypesMap(typedesc, frame, [DPEnumerator class]);
1411         int *offsets = DPCopyOffsetsOfArguments(typedesc);
1412         unsigned numberOfIterations = 0, i;
1413         id result = nil;
1414         
1415         for (i = 2; i < argsCount; i++) {
1416                 if (types[i] == _DP_ENUM_OBJ) {
1417                         unsigned c = [dp_margGetObject(frame, offsets[i], DPEnumerator *) count];
1418                         numberOfIterations = MAX(numberOfIterations, c);
1419                 }
1420         }
1421         
1422         if (numberOfIterations == 0) {
1423                 result = dp_msgSendv(self, sel, frame);
1424         } else {
1425                 char rt;
1426                 
1427                 dp_getReturnType(typedesc, &rt, 1);
1428                 
1429                 // Copy the frame
1430                 size_t fsize = [msg realFrameSize];
1431                 frame = dp_marg_malloc(fsize);
1432                 memcpy(frame, origFrame, fsize);
1433                 
1434                 // Allocate an array for the results
1435                 if (rt == _C_ID || rt == _C_CLASS)
1436                         result = [NSMutableArray arrayWithCapacity:numberOfIterations];
1437                 
1438                 // Let's do it
1439                 for (i = 0; i < numberOfIterations; ++i) {
1440                         DPUpdateEnumereratedArguments(origFrame, frame, types, offsets, argsCount, YES);
1441                         
1442                         if (rt == _C_ID || rt == _C_CLASS)
1443                                 [result addObject:objc_msgSendv(self, sel, frameSize, frame)];
1444                         else
1445                                 objc_msgSendv(self, sel, frameSize, frame);
1446                 }
1447                 
1448                 free(frame);
1449         }
1450         
1451         // Clean up
1452         free(offsets);
1453         free(types);
1454         
1455         return result;
1458 - (id)receive:(DPMessage *)msg {
1459         SEL sel = [msg selector];
1460         marg_list origFrame = [msg arguments], frame = origFrame;
1461         unsigned int frameSize = [msg sizeOfArguments];
1462         const char *typedesc = [self typeEncodingForMessage:msg];
1463         
1464         if (!typedesc)
1465                 return nil;
1466         
1467         unsigned argsCount = dp_getNumberOfArguments(typedesc);
1468         char *types = DPCopyArgumentTypesMap(typedesc, frame, [DPEnumerator class]);
1469         int *offsets = DPCopyOffsetsOfArguments(typedesc);
1470         unsigned numberOfIterations = 0, i;
1471         id result = nil;
1472         
1473         for (i = 2; i < argsCount; i++) {
1474                 if (types[i] == _DP_ENUM_OBJ) {
1475                         unsigned c = [dp_margGetObject(frame, offsets[i], DPEnumerator *) count];
1476                         numberOfIterations = MAX(numberOfIterations, c);
1477                 }
1478         }
1479         
1480         if (numberOfIterations == 0) {
1481                 result = dp_msgSendv(self, sel, frame);
1482         } else {
1483                 char rt;
1484                 
1485                 dp_getReturnType(typedesc, &rt, 1);
1486                 
1487                 // Copy the frame
1488                 size_t fsize = [msg realFrameSize];
1489                 frame = dp_marg_malloc(fsize);
1490                 memcpy(frame, origFrame, fsize);
1491                 
1492                 // Let's do it
1493                 for (i = 0; i < numberOfIterations - 1; ++i) {
1494                         DPUpdateEnumereratedArguments(origFrame, frame, types, offsets, argsCount, YES);
1495                         objc_msgSendv(self, sel, frameSize, frame);
1496                 }
1497                 
1498                 DPUpdateEnumereratedArguments(origFrame, frame, types, offsets, argsCount, YES);
1499                 result = objc_msgSendv(self, sel, frameSize, frame);
1500                 
1501                 free(frame);
1502         }
1503         
1504         // Clean up
1505         free(offsets);
1506         free(types);
1507         
1508         return result;
1511 - (id)ifResponds:(DPMessage *)msg {
1512         if ([self respondsToSelector:[msg selector]])
1513                 return [msg sendTo:self];
1514         return nil;
1517 @end
1519 @implementation NSString (DPHOMCompatibility)
1522  * Given a format string and an initial offset, this function builds a parital types description
1523  * from the given format string. For example, given offset of 8 and a format string "%@",
1524  * the result will be "@8" and the frame size (if not NULL) will be 12 (8 + sizeof(id)).
1525  * If startOffset is 0, the function assumes a method accepting a format string followed
1526  * by variable arguments, in which case it'll also take in account the self and _cmd parameters
1527  * of the method. Otherwise it's up to you to append them at the beginning of the description.
1528  * In any case, you must add the return type of the method at the begnning of the description
1529  * before passing it to anyone (otherwise it won't be a valid decscription).
1530  */
1531 NSMutableString *DPTypeDescriptionForFormatString(NSString *str, unsigned startOffset, unsigned *frameSize) {
1532         // It's probably OK to hardcode the values of @encode() but let's do it the right way
1533         NSMutableString *description = startOffset == 0 ? [NSMutableString stringWithFormat:@"%s0"  // self (offset 0,
1534                                                                                                                                                                                                 // assuming no struct return)
1535                                                                                                                                                                                 "%s%u"  // _cmd
1536                                                                                                                                                                                 "%s%u", // format string
1537                                                                                                                                                 @encode(id),  // @encode(self)
1538                                                                                                                                                 @encode(SEL), // encode(_cmd)
1539                                                                                                                                                 sizeof(id),       // _cmd comes right after self
1540                                                                                                                                                 @encode(id),  // format string
1541                                                                                                                                                 sizeof(id) + sizeof(SEL)]
1542                                                                                                         : [NSMutableString string];
1543         // I assume all constant string these days are constant CFStrings rather than NSConstantString,
1544         // so this *should* be a bit faster than repeatedly calling characterAtIndex: but I haven't
1545         // measured it. Ya ya, premature optimization, evil, whatever.
1546         CFStringInlineBuffer buff;
1547         unsigned i = 1, len = [str length];
1548         unsigned offset = startOffset == 0 ? sizeof(id) * 2 + sizeof(SEL)       // self + _cmd + format
1549                                                                            : startOffset;
1550         UniChar ch1, ch2;
1551         
1552         CFStringInitInlineBuffer((CFStringRef)str, &buff, CFRangeMake(0, len));
1553         ch1 = CFStringGetCharacterFromInlineBuffer(&buff, 0);
1554         
1555         while ((ch2 = CFStringGetCharacterFromInlineBuffer(&buff, i))) {
1556                 if (ch1 == '%') {
1557                         switch (ch2) {
1558                                 case '@':
1559                                         // Note that %@ can also mean a class (which has a different type encodign),
1560                                         // but id should be fine for everyone.
1561                                         [description appendFormat:@"%s%u", @encode(id), offset];
1562                                         offset += sizeof(id);
1563                                         break;
1564                                         
1565                                 case 'd':
1566                                 case 'D':
1567                                 case 'i':
1568                                         // Even though the docs say %d is for "32 bit integer (long)"
1569                                         // we must explicitly use int32_t because OSX64 is LP64 so long
1570                                         // will actually be 64 bit and not 32.
1571                                         [description appendFormat:@"%s%u", @encode(int32_t), offset];
1572                                         offset += sizeof(int32_t);
1573                                         break;
1574                                         
1575                                 case 'u':
1576                                 case 'U':
1577                                 case 'x':
1578                                 case 'X':
1579                                 case 'o':
1580                                 case 'O':
1581                                         [description appendFormat:@"%s%u", @encode(uint32_t), offset];
1582                                         offset += sizeof(uint32_t);
1583                                         break;
1584                                         
1585                                 case 'h': {
1586                                         // Read the next character
1587                                         ++i;
1588                                         ch2 = CFStringGetCharacterFromInlineBuffer(&buff, i);
1589                                         switch (ch2) {
1590                                                 case 'i':
1591                                                         [description appendFormat:@"%s%u", @encode(int16_t), offset];
1592                                                         offset += sizeof(int16_t);
1593                                                         break;
1594                                                         
1595                                                 case 'u':
1596                                                 case 'x':
1597                                                 case 'X':
1598                                                         [description appendFormat:@"%s%u", @encode(uint16_t), offset];
1599                                                         offset += sizeof(uint16_t);
1600                                                         break;
1601                                                         
1602                                                 default:
1603                                                         break;
1604                                         }
1605                                         break;
1606                                 }
1607                                         
1608                                 case 'q': {
1609                                         // Read the next character
1610                                         ++i;
1611                                         ch2 = CFStringGetCharacterFromInlineBuffer(&buff, i);
1612                                         switch (ch2) {
1613                                                 case 'i':
1614                                                         [description appendFormat:@"%s%u", @encode(int64_t), offset];
1615                                                         offset += sizeof(int64_t);
1616                                                         break;
1617                                                         
1618                                                 case 'u':
1619                                                         [description appendFormat:@"%s%u", @encode(uint64_t), offset];
1620                                                         offset += sizeof(uint64_t);
1621                                                         break;
1622                                                         
1623                                                 default:
1624                                                         break;
1625                                         }
1626                                         break;
1627                                 }
1628                                 
1629                                 // Warning: if someone passes a float instead of double
1630                                 // this may cause an overflow somewhere (depends on how
1631                                 // you get our arguments from). How does NSString handle
1632                                 // this?
1633                                 case 'f':
1634                                 case 'e':
1635                                 case 'E':
1636                                 case 'g':
1637                                 case 'G':
1638                                         [description appendFormat:@"%s%u", @encode(double), offset];
1639                                         offset += sizeof(double);
1640                                         break;
1641                                         
1642                                 case 'c':
1643                                         [description appendFormat:@"%s%u", @encode(unsigned char), offset];
1644                                         offset += sizeof(unsigned char);
1645                                         break;
1646                                         
1647                                 case 'C':
1648                                         [description appendFormat:@"%s%u", @encode(unichar), offset];
1649                                         offset += sizeof(unichar);
1650                                         break;
1651                                         
1652                                 case 's':
1653                                         [description appendFormat:@"%s%u", @encode(char *), offset];
1654                                         offset += sizeof(char *);
1655                                         break;
1656                                         
1657                                 case 'S':
1658                                         [description appendFormat:@"%s%u", @encode(unichar *), offset];
1659                                         offset += sizeof(unichar *);
1660                                         break;
1661                                         
1662                                 case 'p':
1663                                         [description appendFormat:@"%s%u", @encode(void *), offset];
1664                                         offset += sizeof(void *);
1665                                         break;
1666                                         
1667                                 default:
1668                                         // No need to reuse this character as it can't be a
1669                                         // valid format specifier.
1670                                         ++i;
1671                                         ch1 = ch2;
1672                                         // goto is *NOT* always evil!!
1673                                         goto NoFormat;
1674                         }
1675                         
1676                         // If we found a foramt specifier there's no need to repeat its
1677                         // last character. Even worse, it may cause wrong behaviour.
1678                         ch1 = CFStringGetCharacterFromInlineBuffer(&buff, ++i);
1679                         ++i;
1680                 } else {
1681 NoFormat:
1682                         ++i;
1683                         ch1 = ch2;
1684                 }
1685         }
1686         
1687         if (frameSize)
1688                 *frameSize = offset + startOffset;
1689         
1690         if (startOffset == 0)
1691                 // Apply the total length of the arguments frame as required by the runtime.
1692                 [description insertString:[NSString stringWithFormat:@"%u", offset]
1693                                                   atIndex:0];
1694         
1695         return description;
1698 // Support for initWithFormat:, stringByAppendingFormat:, stringWithFormat:,
1699 // appendFormat: and initWithFormat:locale:. Fun.
1700 - (const char *)typeEncodingForMessage:(DPMessage *)msg {
1701         SEL sel = [msg selector];
1702         
1703         if (sel == @selector(initWithFormat:) ||
1704                 sel == @selector(stringByAppendingFormat:)||
1705                 sel == @selector(stringWithFormat:))
1706         {
1707                 NSMutableString *desc = DPTypeDescriptionForFormatString(dp_margGetObject([msg arguments], sizeof(id) + sizeof(SEL),
1708                                                                                                                                                                   NSString *), 0, NULL);
1709                 // Set the return type
1710                 [desc insertString:[NSString stringWithCString:@encode(id)]
1711                                    atIndex:0];
1712                 // We return a C string that'll automatically get freed in the
1713                 // next autorelease cycle, together with our mutable string.
1714                 return [desc cStringUsingEncoding:NSASCIIStringEncoding];
1715         } else if (sel == @selector(appendFormat:)) {
1716                 NSMutableString *desc = DPTypeDescriptionForFormatString(dp_margGetObject([msg arguments], sizeof(id) + sizeof(SEL),
1717                                                                                                                                                                   NSString *), 0, NULL);
1718                 // Set the return type
1719                 [desc insertString:[NSString stringWithCString:@encode(void)]
1720                                    atIndex:0];
1721                 // We return a C string that'll automatically get freed in the
1722                 // next autorelease cycle, together with our mutable string.
1723                 return [desc cStringUsingEncoding:NSASCIIStringEncoding];
1724         } else if (sel == @selector(initWithFormat:locale:)) {
1725                 unsigned frameSize;
1726                 NSMutableString *desc = DPTypeDescriptionForFormatString(dp_margGetObject([msg arguments], sizeof(id) + sizeof(SEL),
1727                                                                                                                                                                   NSString *),
1728                                                                                                                                  sizeof(id) * 2 + sizeof(SEL),
1729                                                                                                                                  &frameSize);
1730                 // Build the begining of our description which includes (by order):
1731                 // the return type, total frame size, self + offset, _cmd + offset,
1732                 // format string + offset, locale variable + offset
1733                 [desc insertString:[NSString stringWithFormat:@"%s%u%s0%s%d", @encode(id), frameSize,
1734                         @encode(id), @encode(SEL), sizeof(id) + sizeof(SEL)]
1735                                    atIndex:0];
1736                 // We return a C string that'll automatically get freed in the
1737                 // next autorelease cycle, together with our mutable string.
1738                 return [desc cStringUsingEncoding:NSASCIIStringEncoding];
1739         }
1740         
1741         return [super typeEncodingForMessage:msg];
1744 @end
1747 @implementation NSObject (DPIntrospectionAdditions)
1749 - (const char *)typeEncodingForSelector:(SEL)sel {
1750         Method m = dp_getMethod(self, sel);
1751         return m ? method_getTypeEncoding(m) : NULL;
1754 - (const char *)typeEncodingForMessage:(DPMessage *)msg {
1755         return [self typeEncodingForSelector:[msg selector]];
1760  * OK folks, here is a really cool voodoo.
1761  * As you probably know, the runtime's method lookup for a given class
1762  * scans its methods list and all its super classes, and if it can't find
1763  * a method it tries the instance methods list of the class's base class
1764  * (ya ya, I know it's weird).
1765  * The following code hacks around this behaviour and gets a nicer effect:
1766  * after the class and its super classes couldn't provide a method for
1767  * the selector, it looks up on the instance methods list of our class
1768  * and any of its super class, so the base class's implementation is invoked
1769  * only if non of the direct super classes could provide one.
1770  * This allows us to override only the instance methods in our custom NSObject
1771  * subclass, and still allow proper result when messaging our class.
1772  * See above how it's done with NSString.
1773  */
1774 + (const char *)typeEncodingForSelector:(SEL)sel {
1775         return (void *)(method_getImplementation(class_getInstanceMethod(self, _cmd))(self, _cmd, sel));
1778 + (const char *)typeEncodingForMessage:(DPMessage *)msg {
1779         return (void *)(method_getImplementation(class_getInstanceMethod(self, _cmd))(self, _cmd, msg));
1782 @end