/* ArrayRepId.m Copyright (c) 1998-2009 Philippe Mougin. */ /* This software is open source. See the license. */ #import "build_config.h" #import "ArrayRepId.h" #import "FSArray.h" #import "FScriptFunctions.h" #import #import "FSBooleanPrivate.h" #import "FSCompiler.h" #import "FSVoid.h" #import #import #import "FSBlock.h" #import "BlockRep.h" #import "BlockPrivate.h" #import "BlockInspector.h" #import "FSExecEngine.h" #import "FSPattern.h" #import "FSExecEngine.h" // sendMsg #import "FSNumber.h" #import "NumberPrivate.h" #import #import "ArrayPrivate.h" #import "ArrayRepDouble.h" #import "ArrayRepBoolean.h" #import "FSMiscTools.h" #ifndef MAX #define MAX(a, b) \ ({typeof(a) _a = (a); typeof(b) _b = (b); \ _a > _b ? _a : _b; }) #endif #ifndef MIN #define MIN(a, b) \ ({typeof(a) _a = (a); typeof(b) _b = (b); \ _a < _b ? _a : _b; }) #endif @interface ArrayRepId(ArrayRepIdPrivate) - (void) addObjectsFromFSArray:(FSArray *)otherArray; @end @implementation ArrayRepId /*typedef struct { unsigned long state; id *itemsPtr; unsigned long *mutationsPtr; unsigned long extra[5]; } NSFastEnumerationState;*/ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)statep objects:(id *)stackbuf count:(NSUInteger)len { if (statep->state == 0) { statep->state = 1; statep->itemsPtr = t; statep->mutationsPtr = (unsigned long *)self; return count; } else return 0; } ///////////////////////////////////// USER METHODS - (id)at:(id)index put:(id)elem { double ind; id oldElem; if (!index) FSExecError(@"index of an array must not be nil"); if ([index isKindOfClass:[NSIndexSet class]]) { NSMutableArray *newIndex = [FSArray array]; NSUInteger currentIndex = [index firstIndex]; while (currentIndex != NSNotFound) { [newIndex addObject:[NSNumber numberWithDouble:currentIndex]]; currentIndex = [index indexGreaterThanIndex:currentIndex]; } index = newIndex; } if ([index isKindOfClass:[NSNumber class]]) { ind = [index doubleValue]; if (ind < 0) FSExecError(@"index of an array must be a number greater or equal to 0"); if (ind >= count) FSExecError(@"index of an array must be a number less than the size of the array"); oldElem = t[(NSUInteger)ind]; t[(NSUInteger)ind] = [elem retain]; [oldElem release]; } else if ([index isKindOfClass:[NSArray class]]) { id elem_index; NSUInteger i = 0; NSUInteger j = 0; NSUInteger nb = [index count]; NSUInteger elem_count = 0; // elem_count is initialized in order to avoid a false warning "might be used uninitialized" BOOL elemIsArray = [elem isKindOfClass:[NSArray class]]; //if (![elem isKindOfClass:[NSArray class]]) // FSExecError(@"method \"at:put:\", argument 1 is an array, argument 2 must be an array too"); if (elemIsArray) elem_count = [elem count]; while (i < nb && ![index objectAtIndex:i]) i++; // ignore the nil value if (i == nb) { if ((nb == count || nb == 0) && (!elemIsArray || elem_count == 0)) return elem; else FSExecError(@"invalid index"); } elem_index = [index objectAtIndex:i]; if ([elem_index isKindOfClass:[FSBoolean class]]) { NSUInteger k,trueCount; if (nb != count) FSExecError(@"indexing with an array of boolean of bad size"); if (elemIsArray) { for (k=i, trueCount=0; k trueCount) FSExecError(@"method \"at:put:\", too many elements in argument 2"); } while (i < nb) { elem_index = [index objectAtIndex:i]; if (elem_index == fsTrue || (elem_index != fsFalse && [elem_index isKindOfClass:[FSBoolean class]] && [elem_index isTrue]) ) { oldElem = t[i]; t[i] = [(elemIsArray ? [elem objectAtIndex:j] : elem) retain]; [oldElem release]; j++; } else if (elem_index != fsFalse && ![elem_index isKindOfClass:[FSBoolean class]]) FSExecError(@"indexing with a mixed array"); i++; while (i < nb && ![index objectAtIndex:i]) i++; // ignore the nil value } return elem; } else if ([elem_index isKindOfClass:[NSNumber class]]) { NSUInteger k; if (elemIsArray && nb != elem_count) FSExecError(@"method \"at:put:\", argument 1 and argument 2 must be arrays of same size"); for (k=i; k= count) FSExecError(@"index of an array must be a number less than the size of the array"); } while (i < nb) { elem_index = [index objectAtIndex:i]; ind = [elem_index doubleValue]; oldElem = t[(NSUInteger)ind]; t[(NSUInteger)ind] = [(elemIsArray ? [elem objectAtIndex:i] : elem) retain]; [oldElem release]; i++; while (i < nb && ![index objectAtIndex:i]) i++; // ignore the nil values } } else // elem_index is neither an NSNumber nor a FSBoolean FSExecError([NSString stringWithFormat:@"array indexing by an array containing %@", descriptionForFSMessage(elem_index)]); } else FSExecError([NSString stringWithFormat:@"array indexing by %@, number, array or index set expected", descriptionForFSMessage(index)]); return elem; } - (id)operator_backslash:(FSBlock*)bl // may raise { NSUInteger i; id args[3]; args[0] = t[0]; if ([bl isCompact]) { SEL selector = [bl selector]; NSString *selectorStr = [bl selectorStr]; FSMsgContext *msgContext = [bl msgContext]; args[1] = (id)(selector ? selector : [FSCompiler selectorFromString:selectorStr]); for (i = 1; i < count; i++) { args[2] = t[i]; args[0] = sendMsg(args[0], selector, 3, args, nil, msgContext, nil); } } else { BlockRep *blRep = [bl blockRep]; for (i = 1; i < count; i++) { args[2] = t[i]; args[0] = [blRep body_notCompact_valueArgs:args count:3 block:bl]; } } return args[0]; } - (FSArray *)replicateWithArray:(FSArray *)operand { FSArray *r; NSUInteger i,j; FSArray *index = operand; assert(![operand isProxy]); r = [FSArray array]; switch ([index type]) { case FS_ID: { id *indexData = [operand dataPtr]; for (i=0; i < count; i++) { double opElemDouble; if (![indexData[i] isKindOfClass:NSNumberClass]) FSExecError(@"argument 1 of method \"replicate:\" must be an array of numbers"); opElemDouble = [(NSNumber *)(indexData[i]) doubleValue]; if (opElemDouble < 0) FSExecError(@"argument 1 of method \"replicate:\" must not contain negative numbers"); if (opElemDouble > NSUIntegerMax) FSExecError([NSString stringWithFormat:@"argument of method \"replicate:\" must contain numbers less or equal to %lu", (unsigned long)NSUIntegerMax]); for (j=0; j < opElemDouble; j++) [r addObject:t[i]]; } break; } case DOUBLE: { double *indexData = [(ArrayRepDouble *)[operand arrayRep] doublesPtr]; for (i=0; i < count; i++) { double opElemDouble = indexData[i]; if (opElemDouble < 0) FSExecError(@"argument 1 of method \"replicate:\" must not contain negative numbers"); if (opElemDouble > NSUIntegerMax) FSExecError([NSString stringWithFormat:@"argument of method \"replicate:\" must contain numbers less or equal to %lu", (unsigned long)NSUIntegerMax]); for (j=0; j < opElemDouble; j++) [r addObject:t[i]]; } break; } case BOOLEAN: if ([operand count] != 0) FSExecError(@"argument 1 of method \"replicate:\" is an array of Booleans. An array of numbers was expected"); break; case EMPTY: break; case FETCH_REQUEST: [operand becomeArrayOfId]; return [self indexWithArray:operand]; } // end switch return r; } - (FSArray *)rotatedBy:(NSNumber *)operand { FSArray *r; NSUInteger i; NSInteger op; VERIF_OP_NSNUMBER(@"rotatedBy:"); if (count == 0) return [FSArray array]; op = [operand doubleValue]; r = [FSArray arrayWithCapacity:count]; if (op >= 0) { for(i = op % count ; i < count; i++) [r addObject:t[i]]; for (i = 0; i < op % count; i++) [r addObject:t[i]]; } else { op = -op; for(i = count-(op % count) ; i < count; i++) [r addObject:t[i]]; for (i = 0; i < count-(op % count); i++) [r addObject:t[i]]; } return r; } /////////////////////////////////////////////////////////////////////////// //////////////////////////////////// OTHER METHOD ///////////////////////// /////////////////////////////////////////////////////////////////////////// - (void)addObject:(id)anObject { count++; if (count > capacity) { capacity = (capacity+1)*2; t = (id *)NSReallocateCollectable(t, capacity * sizeof(id), NSScannedOption); } t[count-1] = [anObject retain]; } - (void)addObjectsFromFSArray:(FSArray *)otherArray { NSUInteger i; NSUInteger oldCount = count; id *otherArrayData = [otherArray dataPtr]; NSUInteger otherArrayCount = [otherArray count]; count += otherArrayCount; if (count > capacity) { capacity = count; t = (id *)NSReallocateCollectable(t, capacity * sizeof(id), NSScannedOption); } for (i = 0; i < otherArrayCount; i++) t[oldCount+i] = [otherArrayData[i] retain]; } - (ArrayRepId *) asArrayRepId { return self;} - (id)copyWithZone:(NSZone *)zone { return [[[self class] allocWithZone:zone] initWithObjects:t count:count]; } - (NSUInteger)count { return count; } - (void *)dataPtr {return t;} - (void)dealloc { NSUInteger i; //printf("\n arrayRep : dealloc\n"); for (i = 0; i < count; i++) [t[i] release]; free(t); [super dealloc]; } - (NSString *)descriptionLimited:(NSUInteger)nbElem { NSMutableString *str = [[@"{" mutableCopy] autorelease]; NSString *elemStr = @""; // W NSUInteger i; NSUInteger lim = MIN(count,nbElem); if (lim > 0) { elemStr = printString(t[0]); [str appendString:elemStr]; } for (i = 1; i < lim; i++) { [str appendString:@", "]; if ([elemStr length] > 20) [str appendString:@"\n"]; elemStr = printString(t[i]); [str appendString:elemStr]; } if (count > nbElem) [str appendFormat:@", ... (%lu more elements)",(unsigned long)(count-nbElem)]; [str appendString:@"}"]; return str; } - (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range identical:(BOOL)identical { if (range.length == 0) return NSNotFound; else { NSUInteger i = range.location; NSUInteger end = (range.location + range.length -1); while ( i <= end && !( identical ? t[i] == anObject : ([t[i] isEqual:anObject] || (t[i] == nil && anObject == nil)) ) ) { i++; } return (i > end) ? NSNotFound : i; } } - (id)indexWithArray:(FSArray *)index { switch ([index type]) { case FS_ID: { FSArray *r; id *indexData; NSUInteger i = 0; NSUInteger nb = [index count]; indexData = [index dataPtr]; while (i < nb && !indexData[i]) i++; // ignore the nil value if (i == nb) { if (nb == count || nb == 0) return [FSArray array]; else FSExecError(@"invalid index"); } if (indexData[i] == fsTrue || indexData[i] == fsFalse || [indexData[i] isKindOfClass:[FSBoolean class]]) { if (nb != count) FSExecError(@"indexing with an array of booleans of bad size"); r = [FSArray array]; while (i < nb) { if (indexData[i] == fsTrue) [r addObject:t[i]]; else if (indexData[i] != fsFalse) { if (![indexData[i] isKindOfClass:[FSBoolean class]]) FSExecError(@"indexing with a mixed array"); else if ([indexData[i] isTrue]) [r addObject:t[i]]; } i++; while (i < nb && !indexData[i]) i++; // ignore the nil value } } else if ([indexData[i] isKindOfClass:NSNumberClass]) { r = [FSArray arrayWithCapacity:nb]; while (i < nb) { double ind; if (![indexData[i] isKindOfClass:NSNumberClass]) FSExecError(@"indexing with a mixed array"); ind = [indexData[i] doubleValue]; /* if (ind != (int)ind) FSExecError(@"l'indice d'un tableau doit etre un nombre sans " @"partie fractionnaire"); */ if (ind < 0) FSExecError(@"index of an array must be a number greater or equal to 0"); if (ind >= count) FSExecError(@"index of an array must be a number less than the size of the array"); [r addObject:t[(NSUInteger)ind]]; i++; while (i < nb && !indexData[i]) i++; // ignore the nil value } } else // indexData[i] is neither an NSNumber nor a FSBoolean { FSExecError([NSString stringWithFormat:@"array indexing by an array containing %@", descriptionForFSMessage(indexData[i])]); return nil; // W } return r; } case BOOLEAN: { NSUInteger i; FSArray *r; char *indexData; if ([index count] == 0) return [FSArray array]; if ([index count] != count) FSExecError(@"indexing with an array of booleans of bad size"); r = [FSArray array]; indexData = [(ArrayRepBoolean *)[index arrayRep] booleansPtr]; for (i = 0; i < count; i++) if (indexData[i]) [r addObject:t[i]]; return r; } case DOUBLE: { FSArray *r; double *indexData; NSUInteger i = 0; NSUInteger nb = [index count]; if (i == nb) return [FSArray array]; indexData = [(ArrayRepDouble *)[index arrayRep] doublesPtr]; r = [FSArray arrayWithCapacity:nb]; while (i < nb) { double ind = indexData[i]; /* if (ind != (int)ind) FSExecError(@"l'indice d'un tableau doit etre un nombre sans " @"partie fractionnaire"); */ if (ind < 0) FSExecError(@"index of an array must be a number greater or equal to 0"); if (ind >= count) FSExecError(@"index of an array must be a number less than the size of the array"); [r addObject:t[(NSUInteger)ind]]; i++; } return r; } case EMPTY: return [FSArray array]; case FETCH_REQUEST: [index becomeArrayOfId]; return [self indexWithArray:index]; } // end switch return nil; // W } - (id)init { return [self initWithCapacity:0]; } /*- initFrom:(unsigned)from to:(unsigned)to step:(unsigned)step { if (to < from) return [self init]; if ([self initWithCapacity:1+((to-from)/step)]) { double valcou = from; do { t[count++] = [[[Number alloc] initWithDouble:valcou] retain]; valcou += step; } while (valcou <= to); return self; } return nil; } */ - (id)initFilledWith:(id)elem count:(NSUInteger)nb { if (self = [self initWithCapacity:nb]) { for(count = 0; count < nb; count++) t[count] = [elem retain]; return self; } return nil; } - (id)initWithCapacity:(NSUInteger)aNumItems { if ((self = [super init])) { t = NSAllocateCollectable(aNumItems*sizeof(id), NSScannedOption); if (!t) { [super dealloc]; return nil; } retainCount = 1; capacity = aNumItems; count = 0; return self; } return nil; } - (id)initWithObjectsNoCopy:(id *)tab count:(NSUInteger)nb { if ((self = [super init])) { retainCount = 1; t = tab; capacity = nb; count = nb; return self; } return nil; } - (id)initWithObjects:(id *)objects count:(NSUInteger)nb { NSUInteger i; if (self = [self initWithCapacity:nb]) { for (i = 0; i < nb; i++) { t[i] = [objects[i] retain]; } count = nb; return self; } return nil; } - (void)insertObject:anObject atIndex:(NSUInteger)index { if (index > count) [NSException raise:NSRangeException format:@"index beyond the end of the array in method -insertObject:atIndex"]; count++ ; if (count > capacity) { capacity = (capacity+1)*2; t = (id*)NSReallocateCollectable(t, capacity * sizeof(id), NSScannedOption); } objc_memmove_collectable( &(t[index+1]), &(t[index]), ((count-1)-index) * sizeof(id)); t[index] = [anObject retain]; } - (id)objectAtIndex:(NSUInteger)index { if (index >= count) [NSException raise:NSRangeException format:@"index beyond the end of the array in method -ObjectAtIndex:"]; return t[index]; } - (void)removeLastElem { [self removeLastObject]; } - (void)removeLastObject { if (count == 0) [NSException raise:NSRangeException format:@"-removeLastObject called on an empty array"]; [t[count-1] release]; count--; if (capacity/2 >= count+100) { capacity = capacity/2; t = (id*)NSReallocateCollectable(t, capacity * sizeof(id), NSScannedOption); } } - (void)removeElemAtIndex:(NSUInteger)index { [self removeObjectAtIndex:index]; } - (void)removeObjectAtIndex:(NSUInteger)index { if (index >= count) [NSException raise:NSRangeException format:@"index beyond the end of the array in method -removeObjectAtIndex:"]; [t[index] release]; count--; objc_memmove_collectable( &(t[index]), &(t[index+1]), (count-index) * sizeof(id) ); if (capacity/2 >= count+100) { capacity = capacity/2; t = (id*)NSReallocateCollectable(t, capacity * sizeof(id), NSScannedOption); } } - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { if (index >= count) [NSException raise:NSRangeException format:@"index beyond the end of the array in method -replaceObjectAtIndex:withObject:"]; [anObject retain]; [t[index] release]; t[index] = anObject; } - (id)retain { retainCount++; return self;} - (NSUInteger)retainCount { return retainCount;} - (oneway void)release { if (--retainCount == 0) [self dealloc];} - (NSArray *)subarrayWithRange:(NSRange)range { ArrayRepId *resRep; FSArray *r; resRep = [[ArrayRepId alloc] initWithObjects:t+range.location count:range.length]; r = [FSArray arrayWithRep:resRep]; [resRep release]; return r; } - (enum ArrayRepType)repType { return FS_ID;} - (FSArray *)where:(NSArray *)booleans // precondition: booleans is actualy an array and is of same size as the receiver { FSArray *result = [FSArray array]; if ([booleans isKindOfClass:[FSArray class]] && [(FSArray *)booleans type] == BOOLEAN) { char *rawBooleans = [(ArrayRepBoolean *)[(FSArray *)booleans arrayRep] booleansPtr]; for (NSUInteger i = 0; i < count; i++) if (rawBooleans[i]) [result addObject:t[i]]; } else { id boolean; for (NSUInteger i = 0; i < count; i++) { boolean = [booleans objectAtIndex:i]; if (boolean == fsFalse || boolean == nil) continue; else if (boolean == fsTrue) [result addObject:t[i]]; else if ([boolean isKindOfClass:[FSBoolean class]]) { if ([boolean isTrue]) [result addObject:t[i]]; } else FSExecError(@"argument of method \"where:\" must be an array of booleans"); } } return result; } @end