diff options
-rw-r--r-- | Source/SPDataStorage.m | 116 |
1 files changed, 77 insertions, 39 deletions
diff --git a/Source/SPDataStorage.m b/Source/SPDataStorage.m index 44c2dc9d..a9a608f1 100644 --- a/Source/SPDataStorage.m +++ b/Source/SPDataStorage.m @@ -37,6 +37,7 @@ @interface SPDataStorage (Private_API) - (void) _checkNewRow:(NSMutableArray *)aRow; +- (void) _addRowUnsafeUnchecked:(NSMutableArray *)aRow; @end @@ -107,6 +108,7 @@ static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore /** * Return a mutable array containing the data for a specified row. + * The returned array will be a shallow copy of the internal row object. */ - (NSMutableArray *) rowContentsAtIndex:(NSUInteger)anIndex { @@ -117,12 +119,12 @@ static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore NSMutableArray *editedRow = SPDataStorageGetEditedRow(editedRows, anIndex); if (editedRow != NULL) { - return editedRow; + return [NSMutableArray arrayWithArray:editedRow]; //make a copy to not give away control of our internal state } } // Otherwise, prepare to return the underlying storage row - NSMutableArray *dataArray = SPMySQLResultStoreGetRow(dataStorage, anIndex); + NSMutableArray *dataArray = SPMySQLResultStoreGetRow(dataStorage, anIndex); //returned array is already a copy // Modify unloaded cells as appropriate for (NSUInteger i = 0; i < numberOfColumns; i++) { @@ -252,11 +254,14 @@ static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore // If an edited row exists for the supplied index, use that; otherwise use the underlying // storage row if (state->state < editedRowCount) { - targetRow = SPDataStorageGetEditedRow(editedRows, state->state); + NSMutableArray *internalRow = SPDataStorageGetEditedRow(editedRows, state->state); + if(internalRow != NULL) { + targetRow = [NSMutableArray arrayWithArray:internalRow]; //make a copy to not give away control of our internal state + } } if (targetRow == nil) { - targetRow = SPMySQLResultStoreGetRow(dataStorage, state->state); + targetRow = SPMySQLResultStoreGetRow(dataStorage, state->state); //returned array is already a copy // Modify unloaded cells as appropriate for (NSUInteger i = 0; i < numberOfColumns; i++) { @@ -287,16 +292,18 @@ static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore */ - (void) addRowWithContents:(NSMutableArray *)aRow { - @synchronized(self) { - // Verify the row is of the correct length - [self _checkNewRow:aRow]; - - // Add the new row to the editable store - [editedRows addPointer:aRow]; - editedRowCount++; - - // Update the underlying store as well to keep counts correct - [dataStorage addDummyRow]; + // we can't just store the passed in array as that would give an outsider too much control of our internal state + // (e.g. they could change the bounds after adding it, defeating the check below), so let's make a shallow copy. + NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:aRow]; + @try { + @synchronized(self) { + // Verify the row is of the correct length + [self _checkNewRow:newArray]; + [self _addRowUnsafeUnchecked:newArray]; + } + } + @finally { + [newArray release]; } } @@ -307,39 +314,58 @@ static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore */ - (void) insertRowContents:(NSMutableArray *)aRow atIndex:(NSUInteger)anIndex { - @synchronized(self) { - unsigned long long numberOfRows = SPMySQLResultStoreGetRowCount(dataStorage); - - // Verify the row is of the correct length - [self _checkNewRow:aRow]; - - // Throw an exception if the index is out of bounds - if (anIndex > numberOfRows) { - [NSException raise:NSRangeException format:@"Requested storage index (%llu) beyond bounds (%llu)", (unsigned long long)anIndex, numberOfRows]; - } - - // If "inserting" at the end of the array just add a row - if (anIndex == numberOfRows) { - return [self addRowWithContents:aRow]; + // we can't just store the passed in array as that would give an outsider too much control of our internal state + // (e.g. they could change the bounds after adding it, defeating the check below), so let's make a shallow copy. + NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:aRow]; + @try { + @synchronized(self) { + unsigned long long numberOfRows = SPMySQLResultStoreGetRowCount(dataStorage); + + // Verify the row is of the correct length + [self _checkNewRow:newArray]; + + // Throw an exception if the index is out of bounds + if (anIndex > numberOfRows) { + [NSException raise:NSRangeException format:@"Requested storage index (%llu) beyond bounds (%llu)", (unsigned long long)anIndex, numberOfRows]; + } + + // If "inserting" at the end of the array just add a row + if (anIndex == numberOfRows) { + [self _addRowUnsafeUnchecked:newArray]; + return; + } + + // Add the new row to the editable store + [editedRows insertPointer:newArray atIndex:anIndex]; + editedRowCount++; + + // Update the underlying store to keep counts and indices correct + [dataStorage insertDummyRowAtIndex:anIndex]; } - - // Add the new row to the editable store - [editedRows insertPointer:aRow atIndex:anIndex]; - editedRowCount++; - - // Update the underlying store to keep counts and indices correct - [dataStorage insertDummyRowAtIndex:anIndex]; + } + @finally { + [newArray release]; } } /** * Replace a row with contents of the supplied NSArray. + * + * Note that the supplied objects within the array are retained as a reference rather than copied. */ - (void) replaceRowAtIndex:(NSUInteger)anIndex withRowContents:(NSMutableArray *)aRow { - @synchronized(self) { - [self _checkNewRow:aRow]; - [editedRows replacePointerAtIndex:anIndex withPointer:aRow]; + // we can't just store the passed in array as that would give an outsider too much control of our internal state + // (e.g. they could change the bounds after adding it, defeating the check below), so let's make a shallow copy. + NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:aRow]; + @try { + @synchronized(self) { + [self _checkNewRow:newArray]; + [editedRows replacePointerAtIndex:anIndex withPointer:newArray]; + } + } + @finally { + [newArray release]; } } @@ -357,7 +383,7 @@ static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore // Make sure that the row in question is editable if (editableRow == nil) { - editableRow = [self rowContentsAtIndex:rowIndex]; + editableRow = [self rowContentsAtIndex:rowIndex]; //already returns a copy, so we don't have to go via -replaceRowAtIndex:withRowContents: [editedRows replacePointerAtIndex:rowIndex withPointer:editableRow]; } } @@ -536,4 +562,16 @@ static inline NSMutableArray* SPDataStorageGetEditedRow(NSPointerArray* rowStore } } +// DO NOT CALL THIS METHOD UNLESS YOU CURRENTLY HAVE A LOCK ON SELF!!! +// DO NOT CALL THIS METHOD UNLESS YOU HAVE CALLED _checkNewRow: FIRST! +- (void)_addRowUnsafeUnchecked:(NSMutableArray *)aRow +{ + // Add the new row to the editable store + [editedRows addPointer:aRow]; + editedRowCount++; + + // Update the underlying store as well to keep counts correct + [dataStorage addDummyRow]; +} + @end |