2019-12-19 17:32:49 +00:00
|
|
|
/*
|
2023-04-06 09:44:16 +00:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2016-09-07 22:37:59 +00:00
|
|
|
*
|
2018-05-01 21:33:50 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-09-07 22:37:59 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#import "IGListDiff.h"
|
|
|
|
|
|
|
|
|
|
#import <stack>
|
|
|
|
|
#import <unordered_map>
|
|
|
|
|
#import <vector>
|
|
|
|
|
|
2021-09-01 02:22:18 +00:00
|
|
|
#import "IGListCompatibility.h"
|
2025-05-28 16:28:35 +00:00
|
|
|
#import "IGListMacros.h"
|
2016-09-07 22:37:59 +00:00
|
|
|
|
|
|
|
|
#import "IGListIndexPathResultInternal.h"
|
|
|
|
|
#import "IGListIndexSetResultInternal.h"
|
|
|
|
|
#import "IGListMoveIndexInternal.h"
|
|
|
|
|
#import "IGListMoveIndexPathInternal.h"
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
/// Used to track data stats while diffing.
|
|
|
|
|
struct IGListEntry {
|
|
|
|
|
/// The number of times the data occurs in the old array
|
|
|
|
|
NSInteger oldCounter = 0;
|
|
|
|
|
/// The number of times the data occurs in the new array
|
|
|
|
|
NSInteger newCounter = 0;
|
|
|
|
|
/// The indexes of the data in the old array
|
|
|
|
|
stack<NSInteger> oldIndexes;
|
|
|
|
|
/// Flag marking if the data has been updated between arrays by checking the isEqual: method
|
|
|
|
|
BOOL updated = NO;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Track both the entry and algorithm index. Default the index to NSNotFound
|
|
|
|
|
struct IGListRecord {
|
|
|
|
|
IGListEntry *entry;
|
|
|
|
|
mutable NSInteger index;
|
|
|
|
|
|
|
|
|
|
IGListRecord() {
|
|
|
|
|
entry = NULL;
|
|
|
|
|
index = NSNotFound;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-13 18:07:45 +00:00
|
|
|
static id<NSObject> IGListTableKey(__unsafe_unretained id<IGListDiffable> object) {
|
2016-09-07 22:37:59 +00:00
|
|
|
id<NSObject> key = [object diffIdentifier];
|
|
|
|
|
NSCAssert(key != nil, @"Cannot use a nil key for the diffIdentifier of object %@", object);
|
|
|
|
|
return key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct IGListEqualID {
|
|
|
|
|
bool operator()(const id a, const id b) const {
|
|
|
|
|
return (a == b) || [a isEqual: b];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct IGListHashID {
|
|
|
|
|
size_t operator()(const id o) const {
|
|
|
|
|
return (size_t)[o hash];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-13 18:07:45 +00:00
|
|
|
static void addIndexToMap(BOOL useIndexPaths, NSInteger section, NSInteger index, __unsafe_unretained id<IGListDiffable> object, __unsafe_unretained NSMapTable *map) {
|
2018-02-13 18:07:41 +00:00
|
|
|
id value;
|
|
|
|
|
if (useIndexPaths) {
|
|
|
|
|
value = [NSIndexPath indexPathForItem:index inSection:section];
|
|
|
|
|
} else {
|
|
|
|
|
value = @(index);
|
|
|
|
|
}
|
|
|
|
|
[map setObject:value forKey:[object diffIdentifier]];
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-13 18:07:45 +00:00
|
|
|
static void addIndexToCollection(BOOL useIndexPaths, __unsafe_unretained id collection, NSInteger section, NSInteger index) {
|
2018-02-13 18:07:43 +00:00
|
|
|
if (useIndexPaths) {
|
|
|
|
|
NSIndexPath *path = [NSIndexPath indexPathForItem:index inSection:section];
|
|
|
|
|
[collection addObject:path];
|
|
|
|
|
} else {
|
|
|
|
|
[collection addIndex:index];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
Add diffing optimization when result is all deletes or inserts
Summary:
In an attempt to best other diffing libraries, I noticed that pure insert/delete diffing results would be almost 5x slower than changes between existing arrays. This is pretty much a benchmarking enhancement, but improves diffing performance by ~5x when the from-array or to-array is empty.
```
// OLD only inserts
avg: 0.007469, min: 0.006998, max: 0.016550, p50: 0.007254, p75: 0.007712, p90: 0.007899, p95: 0.008345, p99: 0.016550
// NEW
avg: 0.001392, min: 0.001256, max: 0.006772, p50: 0.001289, p75: 0.001348, p90: 0.001533, p95: 0.001614, p99: 0.006772
```
```
// OLD only deletes
avg: 0.005821, min: 0.005669, max: 0.006511, p50: 0.005766, p75: 0.005852, p90: 0.006030, p95: 0.006204, p99: 0.006511
// NEW
avg: 0.001184, min: 0.001096, max: 0.001673, p50: 0.001123, p75: 0.001212, p90: 0.001378, p95: 0.001467, p99: 0.001673
```
Note the average time improvements (seconds).
Benchmarking done on a 4s w/ this project:
https://pxl.cl/bLBB
Reviewed By: manicakes
Differential Revision: D6968683
fbshipit-source-id: 0d8e058f0aaa9ce756ca69326527d04504ac6429
2018-02-13 18:07:48 +00:00
|
|
|
static NSArray<NSIndexPath *> *indexPathsAndPopulateMap(__unsafe_unretained NSArray<id<IGListDiffable>> *array, NSInteger section, __unsafe_unretained NSMapTable *map) {
|
|
|
|
|
NSMutableArray<NSIndexPath *> *paths = [NSMutableArray new];
|
|
|
|
|
[array enumerateObjectsUsingBlock:^(id<IGListDiffable> obj, NSUInteger idx, BOOL *stop) {
|
|
|
|
|
NSIndexPath *path = [NSIndexPath indexPathForItem:idx inSection:section];
|
|
|
|
|
[paths addObject:path];
|
2019-06-25 18:43:31 +00:00
|
|
|
[map setObject:path forKey:[obj diffIdentifier]];
|
Add diffing optimization when result is all deletes or inserts
Summary:
In an attempt to best other diffing libraries, I noticed that pure insert/delete diffing results would be almost 5x slower than changes between existing arrays. This is pretty much a benchmarking enhancement, but improves diffing performance by ~5x when the from-array or to-array is empty.
```
// OLD only inserts
avg: 0.007469, min: 0.006998, max: 0.016550, p50: 0.007254, p75: 0.007712, p90: 0.007899, p95: 0.008345, p99: 0.016550
// NEW
avg: 0.001392, min: 0.001256, max: 0.006772, p50: 0.001289, p75: 0.001348, p90: 0.001533, p95: 0.001614, p99: 0.006772
```
```
// OLD only deletes
avg: 0.005821, min: 0.005669, max: 0.006511, p50: 0.005766, p75: 0.005852, p90: 0.006030, p95: 0.006204, p99: 0.006511
// NEW
avg: 0.001184, min: 0.001096, max: 0.001673, p50: 0.001123, p75: 0.001212, p90: 0.001378, p95: 0.001467, p99: 0.001673
```
Note the average time improvements (seconds).
Benchmarking done on a 4s w/ this project:
https://pxl.cl/bLBB
Reviewed By: manicakes
Differential Revision: D6968683
fbshipit-source-id: 0d8e058f0aaa9ce756ca69326527d04504ac6429
2018-02-13 18:07:48 +00:00
|
|
|
}];
|
|
|
|
|
return paths;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-07 22:37:59 +00:00
|
|
|
static id IGListDiffing(BOOL returnIndexPaths,
|
|
|
|
|
NSInteger fromSection,
|
|
|
|
|
NSInteger toSection,
|
|
|
|
|
NSArray<id<IGListDiffable>> *oldArray,
|
|
|
|
|
NSArray<id<IGListDiffable>> *newArray,
|
2020-09-08 16:06:16 +00:00
|
|
|
IGListDiffOption option) {
|
2016-09-07 22:37:59 +00:00
|
|
|
const NSInteger newCount = newArray.count;
|
|
|
|
|
const NSInteger oldCount = oldArray.count;
|
|
|
|
|
|
Add diffing optimization when result is all deletes or inserts
Summary:
In an attempt to best other diffing libraries, I noticed that pure insert/delete diffing results would be almost 5x slower than changes between existing arrays. This is pretty much a benchmarking enhancement, but improves diffing performance by ~5x when the from-array or to-array is empty.
```
// OLD only inserts
avg: 0.007469, min: 0.006998, max: 0.016550, p50: 0.007254, p75: 0.007712, p90: 0.007899, p95: 0.008345, p99: 0.016550
// NEW
avg: 0.001392, min: 0.001256, max: 0.006772, p50: 0.001289, p75: 0.001348, p90: 0.001533, p95: 0.001614, p99: 0.006772
```
```
// OLD only deletes
avg: 0.005821, min: 0.005669, max: 0.006511, p50: 0.005766, p75: 0.005852, p90: 0.006030, p95: 0.006204, p99: 0.006511
// NEW
avg: 0.001184, min: 0.001096, max: 0.001673, p50: 0.001123, p75: 0.001212, p90: 0.001378, p95: 0.001467, p99: 0.001673
```
Note the average time improvements (seconds).
Benchmarking done on a 4s w/ this project:
https://pxl.cl/bLBB
Reviewed By: manicakes
Differential Revision: D6968683
fbshipit-source-id: 0d8e058f0aaa9ce756ca69326527d04504ac6429
2018-02-13 18:07:48 +00:00
|
|
|
NSMapTable *oldMap = [NSMapTable strongToStrongObjectsMapTable];
|
|
|
|
|
NSMapTable *newMap = [NSMapTable strongToStrongObjectsMapTable];
|
|
|
|
|
|
|
|
|
|
// if no new objects, everything from the oldArray is deleted
|
|
|
|
|
// take a shortcut and just build a delete-everything result
|
|
|
|
|
if (newCount == 0) {
|
|
|
|
|
if (returnIndexPaths) {
|
|
|
|
|
return [[IGListIndexPathResult alloc] initWithInserts:[NSArray new]
|
|
|
|
|
deletes:indexPathsAndPopulateMap(oldArray, fromSection, oldMap)
|
|
|
|
|
updates:[NSArray new]
|
|
|
|
|
moves:[NSArray new]
|
|
|
|
|
oldIndexPathMap:oldMap
|
|
|
|
|
newIndexPathMap:newMap];
|
|
|
|
|
} else {
|
|
|
|
|
[oldArray enumerateObjectsUsingBlock:^(id<IGListDiffable> obj, NSUInteger idx, BOOL *stop) {
|
|
|
|
|
addIndexToMap(returnIndexPaths, fromSection, idx, obj, oldMap);
|
|
|
|
|
}];
|
|
|
|
|
return [[IGListIndexSetResult alloc] initWithInserts:[NSIndexSet new]
|
|
|
|
|
deletes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, oldCount)]
|
|
|
|
|
updates:[NSIndexSet new]
|
|
|
|
|
moves:[NSArray new]
|
|
|
|
|
oldIndexMap:oldMap
|
|
|
|
|
newIndexMap:newMap];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if no old objects, everything from the newArray is inserted
|
|
|
|
|
// take a shortcut and just build an insert-everything result
|
|
|
|
|
if (oldCount == 0) {
|
|
|
|
|
if (returnIndexPaths) {
|
|
|
|
|
return [[IGListIndexPathResult alloc] initWithInserts:indexPathsAndPopulateMap(newArray, toSection, newMap)
|
|
|
|
|
deletes:[NSArray new]
|
|
|
|
|
updates:[NSArray new]
|
|
|
|
|
moves:[NSArray new]
|
|
|
|
|
oldIndexPathMap:oldMap
|
|
|
|
|
newIndexPathMap:newMap];
|
|
|
|
|
} else {
|
|
|
|
|
[newArray enumerateObjectsUsingBlock:^(id<IGListDiffable> obj, NSUInteger idx, BOOL *stop) {
|
|
|
|
|
addIndexToMap(returnIndexPaths, toSection, idx, obj, newMap);
|
|
|
|
|
}];
|
|
|
|
|
return [[IGListIndexSetResult alloc] initWithInserts:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newCount)]
|
|
|
|
|
deletes:[NSIndexSet new]
|
|
|
|
|
updates:[NSIndexSet new]
|
|
|
|
|
moves:[NSArray new]
|
|
|
|
|
oldIndexMap:oldMap
|
|
|
|
|
newIndexMap:newMap];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-07 22:37:59 +00:00
|
|
|
// symbol table uses the old/new array diffIdentifier as the key and IGListEntry as the value
|
|
|
|
|
// using id<NSObject> as the key provided by https://lists.gnu.org/archive/html/discuss-gnustep/2011-07/msg00019.html
|
|
|
|
|
unordered_map<id<NSObject>, IGListEntry, IGListHashID, IGListEqualID> table;
|
|
|
|
|
|
|
|
|
|
// pass 1
|
|
|
|
|
// create an entry for every item in the new array
|
|
|
|
|
// increment its new count for each occurence
|
|
|
|
|
vector<IGListRecord> newResultsArray(newCount);
|
|
|
|
|
for (NSInteger i = 0; i < newCount; i++) {
|
|
|
|
|
id<NSObject> key = IGListTableKey(newArray[i]);
|
|
|
|
|
IGListEntry &entry = table[key];
|
|
|
|
|
entry.newCounter++;
|
|
|
|
|
|
|
|
|
|
// add NSNotFound for each occurence of the item in the new array
|
|
|
|
|
entry.oldIndexes.push(NSNotFound);
|
|
|
|
|
|
|
|
|
|
// note: the entry is just a pointer to the entry which is stack-allocated in the table
|
|
|
|
|
newResultsArray[i].entry = &entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pass 2
|
|
|
|
|
// update or create an entry for every item in the old array
|
|
|
|
|
// increment its old count for each occurence
|
|
|
|
|
// record the original index of the item in the old array
|
|
|
|
|
// MUST be done in descending order to respect the oldIndexes stack construction
|
|
|
|
|
vector<IGListRecord> oldResultsArray(oldCount);
|
|
|
|
|
for (NSInteger i = oldCount - 1; i >= 0; i--) {
|
|
|
|
|
id<NSObject> key = IGListTableKey(oldArray[i]);
|
|
|
|
|
IGListEntry &entry = table[key];
|
|
|
|
|
entry.oldCounter++;
|
|
|
|
|
|
2017-05-25 15:02:48 +00:00
|
|
|
// push the original indices where the item occurred onto the index stack
|
2016-09-07 22:37:59 +00:00
|
|
|
entry.oldIndexes.push(i);
|
|
|
|
|
|
|
|
|
|
// note: the entry is just a pointer to the entry which is stack-allocated in the table
|
|
|
|
|
oldResultsArray[i].entry = &entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pass 3
|
|
|
|
|
// handle data that occurs in both arrays
|
|
|
|
|
for (NSInteger i = 0; i < newCount; i++) {
|
|
|
|
|
IGListEntry *entry = newResultsArray[i].entry;
|
|
|
|
|
|
|
|
|
|
// grab and pop the top original index. if the item was inserted this will be NSNotFound
|
Minimum fixes for Xcode 9.3 build errors
Summary:
Issue fixed: #1141
This is a set of minimum fixes for Xcode 9.3 rather than #1142. Much smaller diffs 😃
The changes here were done by Xcode's FixIt. See also https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW5.
> Platform Dependencies
>
> OS X uses several data types—NSInteger, NSUInteger,CGFloat, and CFIndex—to provide a consistent means of representing values in 32- and 64-bit environments. In a 32-bit environment, NSInteger and NSUInteger are defined as int and unsigned int, respectively. In 64-bit environments, NSInteger and NSUInteger are defined as long and unsigned long, respectively. To avoid the need to use different printf-style type specifiers depending on the platform, you can use the specifiers shown in Table 3. Note that in some cases you may have to cast the value.
- [x] All tests pass. Demo project builds and runs.
- [x] I added tests, an experiment, or detailed why my change isn't tested.
- [x] I added an entry to the `CHANGELOG.md` for any breaking changes, enhancements, or bug fixes.
- [x] I have reviewed the [contributing guide](https://github.com/Instagram/IGListKit/blob/master/.github/CONTRIBUTING.md)
Closes https://github.com/Instagram/IGListKit/pull/1143
Differential Revision: D7551617
Pulled By: rnystrom
fbshipit-source-id: b27ebe2b1a1c93ebfd1218dc38b7621fefee2e6b
2018-04-09 15:06:47 +00:00
|
|
|
NSCAssert(!entry->oldIndexes.empty(), @"Old indexes is empty while iterating new item %li. Should have NSNotFound", (long)i);
|
2016-09-07 22:37:59 +00:00
|
|
|
const NSInteger originalIndex = entry->oldIndexes.top();
|
|
|
|
|
entry->oldIndexes.pop();
|
|
|
|
|
|
|
|
|
|
if (originalIndex < oldCount) {
|
|
|
|
|
const id<IGListDiffable> n = newArray[i];
|
|
|
|
|
const id<IGListDiffable> o = oldArray[originalIndex];
|
|
|
|
|
switch (option) {
|
|
|
|
|
case IGListDiffPointerPersonality:
|
|
|
|
|
// flag the entry as updated if the pointers are not the same
|
|
|
|
|
if (n != o) {
|
|
|
|
|
entry->updated = YES;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case IGListDiffEquality:
|
2016-11-20 16:25:34 +00:00
|
|
|
// use -[IGListDiffable isEqualToDiffableObject:] between both version of data to see if anything has changed
|
2016-09-07 22:37:59 +00:00
|
|
|
// skip the equality check if both indexes point to the same object
|
2016-11-11 00:55:33 +00:00
|
|
|
if (n != o && ![n isEqualToDiffableObject:o]) {
|
2016-09-07 22:37:59 +00:00
|
|
|
entry->updated = YES;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2025-05-28 16:28:35 +00:00
|
|
|
default /* unexpected */:
|
|
|
|
|
IGLK_UNEXPECTED_SWITCH_CASE_ABORT(IGListDiffOption, option);
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (originalIndex != NSNotFound
|
|
|
|
|
&& entry->newCounter > 0
|
|
|
|
|
&& entry->oldCounter > 0) {
|
|
|
|
|
// if an item occurs in the new and old array, it is unique
|
|
|
|
|
// assign the index of new and old records to the opposite index (reverse lookup)
|
|
|
|
|
newResultsArray[i].index = originalIndex;
|
|
|
|
|
oldResultsArray[originalIndex].index = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// storage for final NSIndexPaths or indexes
|
|
|
|
|
id mInserts, mMoves, mUpdates, mDeletes;
|
|
|
|
|
if (returnIndexPaths) {
|
|
|
|
|
mInserts = [NSMutableArray<NSIndexPath *> new];
|
|
|
|
|
mMoves = [NSMutableArray<IGListMoveIndexPath *> new];
|
|
|
|
|
mUpdates = [NSMutableArray<NSIndexPath *> new];
|
|
|
|
|
mDeletes = [NSMutableArray<NSIndexPath *> new];
|
|
|
|
|
} else {
|
|
|
|
|
mInserts = [NSMutableIndexSet new];
|
2019-05-21 02:10:35 +00:00
|
|
|
mMoves = [NSMutableArray<IGListMoveIndex *> new];
|
2016-09-07 22:37:59 +00:00
|
|
|
mUpdates = [NSMutableIndexSet new];
|
|
|
|
|
mDeletes = [NSMutableIndexSet new];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// track offsets from deleted items to calculate where items have moved
|
|
|
|
|
vector<NSInteger> deleteOffsets(oldCount), insertOffsets(newCount);
|
|
|
|
|
NSInteger runningOffset = 0;
|
|
|
|
|
|
|
|
|
|
// iterate old array records checking for deletes
|
|
|
|
|
// incremement offset for each delete
|
|
|
|
|
for (NSInteger i = 0; i < oldCount; i++) {
|
|
|
|
|
deleteOffsets[i] = runningOffset;
|
|
|
|
|
const IGListRecord record = oldResultsArray[i];
|
|
|
|
|
// if the record index in the new array doesn't exist, its a delete
|
|
|
|
|
if (record.index == NSNotFound) {
|
2018-02-13 18:07:43 +00:00
|
|
|
addIndexToCollection(returnIndexPaths, mDeletes, fromSection, i);
|
2016-09-07 22:37:59 +00:00
|
|
|
runningOffset++;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-13 18:07:41 +00:00
|
|
|
addIndexToMap(returnIndexPaths, fromSection, i, oldArray[i], oldMap);
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reset and track offsets from inserted items to calculate where items have moved
|
|
|
|
|
runningOffset = 0;
|
|
|
|
|
|
|
|
|
|
for (NSInteger i = 0; i < newCount; i++) {
|
|
|
|
|
insertOffsets[i] = runningOffset;
|
|
|
|
|
const IGListRecord record = newResultsArray[i];
|
|
|
|
|
const NSInteger oldIndex = record.index;
|
|
|
|
|
// add to inserts if the opposing index is NSNotFound
|
|
|
|
|
if (record.index == NSNotFound) {
|
2018-02-13 18:07:43 +00:00
|
|
|
addIndexToCollection(returnIndexPaths, mInserts, toSection, i);
|
2016-09-07 22:37:59 +00:00
|
|
|
runningOffset++;
|
|
|
|
|
} else {
|
|
|
|
|
// note that an entry can be updated /and/ moved
|
|
|
|
|
if (record.entry->updated) {
|
2018-02-13 18:07:43 +00:00
|
|
|
addIndexToCollection(returnIndexPaths, mUpdates, fromSection, oldIndex);
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// calculate the offset and determine if there was a move
|
|
|
|
|
// if the indexes match, ignore the index
|
|
|
|
|
const NSInteger insertOffset = insertOffsets[i];
|
|
|
|
|
const NSInteger deleteOffset = deleteOffsets[oldIndex];
|
|
|
|
|
if ((oldIndex - deleteOffset + insertOffset) != i) {
|
|
|
|
|
id move;
|
|
|
|
|
if (returnIndexPaths) {
|
|
|
|
|
NSIndexPath *from = [NSIndexPath indexPathForItem:oldIndex inSection:fromSection];
|
|
|
|
|
NSIndexPath *to = [NSIndexPath indexPathForItem:i inSection:toSection];
|
|
|
|
|
move = [[IGListMoveIndexPath alloc] initWithFrom:from to:to];
|
|
|
|
|
} else {
|
|
|
|
|
move = [[IGListMoveIndex alloc] initWithFrom:oldIndex to:i];
|
|
|
|
|
}
|
2016-10-14 18:25:59 +00:00
|
|
|
[mMoves addObject:move];
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-13 18:07:41 +00:00
|
|
|
addIndexToMap(returnIndexPaths, toSection, i, newArray[i], newMap);
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-11 19:19:53 +00:00
|
|
|
NSCAssert((oldCount + (NSInteger)[mInserts count] - (NSInteger)[mDeletes count]) == newCount,
|
2018-07-25 19:14:45 +00:00
|
|
|
@"Sanity check failed applying %lu inserts and %lu deletes to old count %li equaling new count %li",
|
|
|
|
|
(unsigned long)[mInserts count], (unsigned long)[mDeletes count], (long)oldCount, (long)newCount);
|
2016-09-07 22:37:59 +00:00
|
|
|
|
|
|
|
|
if (returnIndexPaths) {
|
|
|
|
|
return [[IGListIndexPathResult alloc] initWithInserts:mInserts
|
|
|
|
|
deletes:mDeletes
|
|
|
|
|
updates:mUpdates
|
|
|
|
|
moves:mMoves
|
|
|
|
|
oldIndexPathMap:oldMap
|
|
|
|
|
newIndexPathMap:newMap];
|
|
|
|
|
} else {
|
|
|
|
|
return [[IGListIndexSetResult alloc] initWithInserts:mInserts
|
|
|
|
|
deletes:mDeletes
|
|
|
|
|
updates:mUpdates
|
|
|
|
|
moves:mMoves
|
|
|
|
|
oldIndexMap:oldMap
|
|
|
|
|
newIndexMap:newMap];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IGListIndexSetResult *IGListDiff(NSArray<id<IGListDiffable> > *oldArray,
|
|
|
|
|
NSArray<id<IGListDiffable>> *newArray,
|
|
|
|
|
IGListDiffOption option) {
|
2020-09-08 16:06:16 +00:00
|
|
|
return IGListDiffing(NO, 0, 0, oldArray, newArray, option);
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IGListIndexPathResult *IGListDiffPaths(NSInteger fromSection,
|
|
|
|
|
NSInteger toSection,
|
|
|
|
|
NSArray<id<IGListDiffable>> *oldArray,
|
|
|
|
|
NSArray<id<IGListDiffable>> *newArray,
|
|
|
|
|
IGListDiffOption option) {
|
2020-09-08 16:06:16 +00:00
|
|
|
return IGListDiffing(YES, fromSection, toSection, oldArray, newArray, option);
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|