// // HOMCArrayIterations.m // HigherOrderMessaging // // Created by Ofri Wolfus on 18/09/06. // Copyright 2006 Ofri Wolfus. All rights reserved. // #import "HOMCArrayIterations.h" #import "Message.h" #import "HOMIteratedArgument.h" #import "HOMUtilities.h" #include #include #include extern Method _hom_findMethodForSelector(SEL); id hom_collectv(id *objects, unsigned count, BOOL cleanup, Class resultingCollection, Message *msg) { SEL sel = [msg selector]; marg_list origArgs = [msg arguments]; unsigned argsSize = [msg argumentsSize]; marg_list args = malloc(malloc_size(origArgs)); unsigned argCount = hom_getNumberOfArguments(sel); unsigned i; Class iteratedArgCls = [HOMIteratedArgument class]; char *types = calloc(argCount, sizeof(char)); Method meth = _hom_findMethodForSelector(sel); NSMutableArray *result = nil; // This may actually be a mutable set or any other collection IMP addObject = NULL; if (*(method_getTypeEncoding(meth)) == _C_ID || *(method_getTypeEncoding(meth)) == _C_CLASS) { result = [[[resultingCollection alloc] initWithCapacity:count] autorelease]; addObject = [result methodForSelector:@selector(addObject:)]; } // First, copy the arguments list since we're going to modify it memcpy(args, origArgs, malloc_size(args)); // Scan the arguments of our message and find instances of HOMIteratedArgument. // Our types array will mark the places of these with the letter 'z' so we don't have to check again. // This loop also finds the largest number of arguments that needs to be iterated, and stores it in count. for (i = 2U; i < argCount; i++) { types[i] = *hom_getArgumentTypeAtIndex(meth, i); if (types[i] == _C_ID) { if ([marg_getValue(args, hom_getArgumentOffsetAtIndex(meth, i), id) isKindOfClass:iteratedArgCls]) types[i] = 'z'; // This is our special mark for an iterated argument } } // Loop through all objects in all our iterated arguments, update our args list, and resend it for (i = 0U; i < count; i++) { unsigned j; // Update the remaining arguments in our list for (j = 2U; j < argCount; j++) { if (types[j] == 'z') { int offset = hom_getArgumentOffsetAtIndex(meth, j); HOMIteratedArgument *arg = marg_getValue(origArgs, offset, HOMIteratedArgument *); marg_setValue(args, offset, id, [arg nextObject]); // When there are no arguments left for this argument iterator, we mark its type as id // so we'll skip it in the next run. if ([arg isFinished]) types[j] = _C_ID; } } // Send the updated arguments list addObject(result, @selector(addObject:), objc_msgSendv(objects[i], sel, argsSize, args)); } // Clean up free(types); free(args); if (cleanup) free(objects); return result; } /* * NOTE: Although hom_returnWhere() and hom_returnSingleWhere() are defined as static inline, they can never be inlined. * When support for inlined functions that take arguments list is implemented, uncomment the line which says they should always be inlined. */ //static inline id hom_returnWhere(id *objects, unsigned count, BOOL resultVal, Message *firstMsg, va_list additionalMessages) __attribute__((always_inline)); static inline id hom_returnWhere(id *objects, unsigned count, BOOL cleanup, Class resultingCollection, BOOL resultVal, Message *firstMsg, va_list additionalMessages) { Message *msg; unsigned i; NSMutableArray *result = [[resultingCollection alloc] init]; // This may actually be a mutable set or any other collection IMP addObject = [result methodForSelector:@selector(addObject:)]; va_list messages; // Loop through all our objects, and send it all the messages passed to us. // All messages except the last are assumed to return id, and the last one should return a BOOL // (although any integer value will work). for (i = 0U; i < count; i++) { id obj = objects[i]; // Send every message except the last one to our object while setting obj // to be the result of the last message. // NOTE: We don't verify that the receiver responds to the actual message like -select does // which give us a nice speed boost, but may also cause an exception to be thrown. // TODO: Cache the IMPs of -[Message selector], -[Message argumentsSize] and -[Message arguments]. //for (j = 0U; j < (msgCount - 1U); j++) { obj = objc_msgSendv(obj, [firstMsg selector], [firstMsg argumentsSize], [firstMsg arguments]); va_copy(messages, additionalMessages); while ((msg = va_arg(messages, Message *))) obj = objc_msgSendv(obj, [msg selector], [msg argumentsSize], [msg arguments]); va_end(messages); //msg = objectAtIndexIMP(messages, @selector(objectAtIndex:), msgCount - 1); //if (((hom_BOOL_msgSendv)objc_msgSendv)(obj, [msg selector], [msg argumentsSize], [msg arguments]) == resultValue) // [result addObject:object]; if ((BOOL)(int)obj == resultVal) addObject(result, @selector(addObject:), objects[i]); } // Clean up if (cleanup) { free(objects); va_end(additionalMessages); } // Make our return value pretty if ([result count] == 0) { [result release]; result = nil; } else { result = [result autorelease]; } return result; } id hom_selectWhere(id *objects, unsigned count, BOOL cleanup, Class resultingCollection, Message *firstMsg, va_list additionalMessages) { return hom_returnWhere(objects, count, cleanup, resultingCollection, YES, firstMsg, additionalMessages); } id hom_rejectWhere(id *objects, unsigned count, BOOL cleanup, Class resultingCollection, Message *firstMsg, va_list additionalMessages) { return hom_returnWhere(objects, count, cleanup, resultingCollection, NO, firstMsg, additionalMessages); } //static inline id hom_returnSingleWhere(id *objects, unsigned count, BOOL resultVal, Message *firstMsg, va_list additionalMessages) __attribute__((always_inline)); static inline id hom_returnSingleWhere(id *objects, unsigned count, BOOL cleanup, BOOL resultVal, Message *firstMsg, va_list additionalMessages) { Message *msg; unsigned i; va_list messages; // Loop through all our objects, and send it all the messages passed to us. // All messages except the last are assumed to return id, and the last one should return a BOOL // (although any integer value will work). for (i = 0U; i < count; i++) { id obj = objects[i]; // Send every message except the last one to our object while setting obj // to be the result of the last message. // NOTE: We don't verify that the receiver responds to the actual message like -select does // which give us a nice speed boost, but may also cause an exception to be thrown. // TODO: Cache the IMPs of -[Message selector], -[Message argumentsSize] and -[Message arguments]. //for (j = 0U; j < (msgCount - 1U); j++) { obj = objc_msgSendv(obj, [firstMsg selector], [firstMsg argumentsSize], [firstMsg arguments]); va_copy(messages, additionalMessages); while ((msg = va_arg(messages, Message *))) obj = objc_msgSendv(obj, [msg selector], [msg argumentsSize], [msg arguments]); va_end(messages); //msg = objectAtIndexIMP(messages, @selector(objectAtIndex:), msgCount - 1); //if (((hom_BOOL_msgSendv)objc_msgSendv)(obj, [msg selector], [msg argumentsSize], [msg arguments]) == resultValue) // [result addObject:object]; if ((BOOL)(int)obj == resultVal) { obj = objects[i]; if (cleanup) { free(objects); va_end(additionalMessages); } return obj; } } if (cleanup) { free(objects); va_end(additionalMessages); } return nil; } id hom_selectSingleWhere(id *objects, unsigned count, BOOL cleanup, Message *firstMsg, va_list additionalMessages) { return hom_returnSingleWhere(objects, count, cleanup, YES, firstMsg, additionalMessages); } /*id hom_rejectSingleWhere(id *objects, unsigned count, BOOL cleanup, Message *firstMsg, va_list additionalMessages) { return hom_returnSingleWhere(objects, count, cleanup, NO, firstMsg, additionalMessages); }*/