mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Introduce ASIntegerMap, improve our changeset handling #trivial (#405)
* Introduce ASIntegerMap, improve our changeset handling Rename to ASIntegerMap License header * Add unit tests for ASIntegerMap * Address nit
This commit is contained in:
@@ -327,7 +327,7 @@
|
||||
CC0F886D1E4286FA00576FED /* ReferenceImages_iOS_10 in Resources */ = {isa = PBXBuildFile; fileRef = CC0F886A1E4286FA00576FED /* ReferenceImages_iOS_10 */; };
|
||||
CC11F97A1DB181180024D77B /* ASNetworkImageNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */; };
|
||||
CC2F65EE1E5FFB1600DA57C9 /* ASMutableElementMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */; };
|
||||
CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.mm */; };
|
||||
CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */; };
|
||||
CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20811C3F76D600798563 /* ASPendingStateController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CC3B20861C3F76D600798563 /* ASPendingStateController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20821C3F76D600798563 /* ASPendingStateController.mm */; };
|
||||
CC3B208A1C3F7A5400798563 /* ASWeakSet.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20871C3F7A5400798563 /* ASWeakSet.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -343,6 +343,8 @@
|
||||
CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */; };
|
||||
CC55A7111E52A0F200594372 /* ASResponderChainEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */; };
|
||||
CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */; };
|
||||
CC56013B1F06E9A700DC4FBE /* ASIntegerMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */; };
|
||||
CC56013C1F06E9A700DC4FBE /* ASIntegerMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */; };
|
||||
CC57EAF71E3939350034C595 /* ASCollectionView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CC583AD61EF9BDBE00134156 /* ASTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = CC583AC21EF9BAB400134156 /* ASTestCase.m */; };
|
||||
@@ -399,6 +401,7 @@
|
||||
CCCCCCE71EC3F0FC0087FE10 /* NSAttributedString+ASText.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCE51EC3F0FC0087FE10 /* NSAttributedString+ASText.h */; };
|
||||
CCCCCCE81EC3F0FC0087FE10 /* NSAttributedString+ASText.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCE61EC3F0FC0087FE10 /* NSAttributedString+ASText.m */; };
|
||||
CCDD148B1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */; };
|
||||
CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */; };
|
||||
CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */ = {isa = PBXBuildFile; fileRef = DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; };
|
||||
@@ -788,7 +791,7 @@
|
||||
CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageNodeTests.m; sourceTree = "<group>"; };
|
||||
CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionView+Undeprecated.h"; sourceTree = "<group>"; };
|
||||
CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMutableElementMap.h; sourceTree = "<group>"; };
|
||||
CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMutableElementMap.mm; sourceTree = "<group>"; };
|
||||
CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASMutableElementMap.m; sourceTree = "<group>"; };
|
||||
CC3B20811C3F76D600798563 /* ASPendingStateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPendingStateController.h; sourceTree = "<group>"; };
|
||||
CC3B20821C3F76D600798563 /* ASPendingStateController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPendingStateController.mm; sourceTree = "<group>"; };
|
||||
CC3B20871C3F7A5400798563 /* ASWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakSet.h; sourceTree = "<group>"; };
|
||||
@@ -807,6 +810,8 @@
|
||||
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+AsyncDisplayKit.m"; sourceTree = "<group>"; };
|
||||
CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASResponderChainEnumerator.h; sourceTree = "<group>"; };
|
||||
CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASResponderChainEnumerator.m; sourceTree = "<group>"; };
|
||||
CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIntegerMap.h; sourceTree = "<group>"; };
|
||||
CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASIntegerMap.mm; sourceTree = "<group>"; };
|
||||
CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
CC583AC01EF9BAB400134156 /* ASDisplayNode+OCMock.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "ASDisplayNode+OCMock.m"; sourceTree = "<group>"; };
|
||||
CC583AC11EF9BAB400134156 /* ASTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASTestCase.h; sourceTree = "<group>"; };
|
||||
@@ -873,6 +878,7 @@
|
||||
CCE04B201E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IGListAdapter+AsyncDisplayKit.h"; sourceTree = "<group>"; };
|
||||
CCE04B211E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "IGListAdapter+AsyncDisplayKit.m"; sourceTree = "<group>"; };
|
||||
CCE04B2B1E314A32006AEBBB /* ASSupplementaryNodeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASSupplementaryNodeSource.h; sourceTree = "<group>"; };
|
||||
CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIntegerMapTests.m; sourceTree = "<group>"; };
|
||||
D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
|
||||
D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = "<group>"; };
|
||||
@@ -1132,6 +1138,7 @@
|
||||
CC034A0F1E60C9BF00626263 /* ASRectTableTests.m */,
|
||||
CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */,
|
||||
CC051F1E1D7A286A006434CB /* ASCALayerTests.m */,
|
||||
CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */,
|
||||
CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */,
|
||||
CC8B05D41D73836400F54286 /* ASPerformanceTestContext.h */,
|
||||
CC8B05D51D73836400F54286 /* ASPerformanceTestContext.m */,
|
||||
@@ -1210,6 +1217,8 @@
|
||||
058D09E1195D050800B7D73C /* Details */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */,
|
||||
CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */,
|
||||
CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */,
|
||||
CC0F885D1E4280B800576FED /* _ASCollectionViewCell.m */,
|
||||
3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */,
|
||||
@@ -1318,7 +1327,7 @@
|
||||
CCA282B21E9EA7310037E8B7 /* ASTipsController.h */,
|
||||
CCA282B31E9EA7310037E8B7 /* ASTipsController.m */,
|
||||
CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */,
|
||||
CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.mm */,
|
||||
CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */,
|
||||
E5ABAC791E8564EE007AC15C /* ASRectTable.h */,
|
||||
E5ABAC7A1E8564EE007AC15C /* ASRectTable.m */,
|
||||
CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */,
|
||||
@@ -1713,6 +1722,7 @@
|
||||
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */,
|
||||
B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */,
|
||||
68FC85E31CE29B7E00EDD713 /* ASTabBarController.h in Headers */,
|
||||
CC56013B1F06E9A700DC4FBE /* ASIntegerMap.h in Headers */,
|
||||
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */,
|
||||
E54E81FC1EB357BD00FFE8E1 /* ASPageTable.h in Headers */,
|
||||
B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */,
|
||||
@@ -2089,6 +2099,7 @@
|
||||
69FEE53D1D95A9AF0086F066 /* ASLayoutElementStyleTests.m in Sources */,
|
||||
CC4981B31D1A02BE004E13CC /* ASTableViewThrashTests.m in Sources */,
|
||||
CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */,
|
||||
CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */,
|
||||
058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */,
|
||||
83A7D95E1D446A6E00BF333E /* ASWeakMapTests.m in Sources */,
|
||||
056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */,
|
||||
@@ -2210,7 +2221,7 @@
|
||||
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */,
|
||||
9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.m in Sources */,
|
||||
CC0F885F1E4280B800576FED /* _ASCollectionViewCell.m in Sources */,
|
||||
CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.mm in Sources */,
|
||||
CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.m in Sources */,
|
||||
B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */,
|
||||
E58E9E4A1E941DA5004CFC59 /* ASCollectionLayout.mm in Sources */,
|
||||
6947B0C01E36B4E30007C478 /* ASStackUnpositionedLayout.mm in Sources */,
|
||||
@@ -2279,6 +2290,7 @@
|
||||
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */,
|
||||
CCCCCCE21EC3EF060087FE10 /* ASTextUtilities.m in Sources */,
|
||||
CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */,
|
||||
CC56013C1F06E9A700DC4FBE /* ASIntegerMap.mm in Sources */,
|
||||
697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */,
|
||||
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
|
||||
CCA282CD1E9EB73E0037E8B7 /* ASTipNode.m in Sources */,
|
||||
|
||||
@@ -703,7 +703,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCollectionElement *> *
|
||||
}
|
||||
|
||||
// Migrate old supplementary nodes to their new index paths.
|
||||
[map migrateSupplementaryElementsWithChangeSet:changeSet];
|
||||
[map migrateSupplementaryElementsWithSectionMapping:changeSet.sectionMapping];
|
||||
|
||||
for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) {
|
||||
[map removeItemsAtIndexPaths:change.indexPaths];
|
||||
|
||||
70
Source/Details/ASIntegerMap.h
Normal file
70
Source/Details/ASIntegerMap.h
Normal file
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// ASIntegerMap.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* An objective-C wrapper for unordered_map.
|
||||
*/
|
||||
AS_SUBCLASSING_RESTRICTED
|
||||
@interface ASIntegerMap : NSObject <NSCopying>
|
||||
|
||||
/**
|
||||
* Creates a map based on the specified update to an array.
|
||||
*
|
||||
* If oldCount is 0, returns the empty map.
|
||||
* If deleted and inserted are empty, returns the identity map.
|
||||
*/
|
||||
+ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount
|
||||
deleted:(nullable NSIndexSet *)deleted
|
||||
inserted:(nullable NSIndexSet *)inserted;
|
||||
|
||||
/**
|
||||
* A singleton that maps each integer to itself. Its inverse is itself.
|
||||
*
|
||||
* Note: You cannot mutate this.
|
||||
*/
|
||||
@property (class, atomic, readonly) ASIntegerMap *identityMap;
|
||||
|
||||
/**
|
||||
* A singleton that returns NSNotFound for all keys. Its inverse is itself.
|
||||
*
|
||||
* Note: You cannot mutate this.
|
||||
*/
|
||||
@property (class, atomic, readonly) ASIntegerMap *emptyMap;
|
||||
|
||||
/**
|
||||
* Retrieves the integer for a given key, or NSNotFound if the key is not found.
|
||||
*
|
||||
* @param key A key to lookup the value for.
|
||||
*/
|
||||
- (NSInteger)integerForKey:(NSInteger)key;
|
||||
|
||||
/**
|
||||
* Sets the value for a given key.
|
||||
*
|
||||
* @param value The new value.
|
||||
* @param key The key to store the value for.
|
||||
*/
|
||||
- (void)setInteger:(NSInteger)value forKey:(NSInteger)key;
|
||||
|
||||
/**
|
||||
* Create and return a map with the inverse mapping.
|
||||
*/
|
||||
- (ASIntegerMap *)inverseMap;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
188
Source/Details/ASIntegerMap.mm
Normal file
188
Source/Details/ASIntegerMap.mm
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// ASIntegerMap.mm
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "ASIntegerMap.h"
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
#import <unordered_map>
|
||||
#import <NSIndexSet+ASHelpers.h>
|
||||
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
||||
|
||||
/**
|
||||
* This is just a friendly Objective-C interface to unordered_map<NSInteger, NSInteger>
|
||||
*/
|
||||
@interface ASIntegerMap () <ASDescriptionProvider>
|
||||
@end
|
||||
|
||||
@implementation ASIntegerMap {
|
||||
std::unordered_map<NSInteger, NSInteger> _map;
|
||||
BOOL _isIdentity;
|
||||
BOOL _isEmpty;
|
||||
BOOL _immutable; // identity map and empty mape are immutable.
|
||||
}
|
||||
|
||||
#pragma mark - Singleton
|
||||
|
||||
+ (ASIntegerMap *)identityMap
|
||||
{
|
||||
static ASIntegerMap *identityMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
identityMap = [[ASIntegerMap alloc] init];
|
||||
identityMap->_isIdentity = YES;
|
||||
identityMap->_immutable = YES;
|
||||
});
|
||||
return identityMap;
|
||||
}
|
||||
|
||||
+ (ASIntegerMap *)emptyMap
|
||||
{
|
||||
static ASIntegerMap *emptyMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
emptyMap = [[ASIntegerMap alloc] init];
|
||||
emptyMap->_isEmpty = YES;
|
||||
emptyMap->_immutable = YES;
|
||||
});
|
||||
return emptyMap;
|
||||
}
|
||||
|
||||
+ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount deleted:(NSIndexSet *)deletions inserted:(NSIndexSet *)insertions
|
||||
{
|
||||
if (oldCount == 0) {
|
||||
return ASIntegerMap.emptyMap;
|
||||
}
|
||||
|
||||
if (deletions.count == 0 && insertions.count == 0) {
|
||||
return ASIntegerMap.identityMap;
|
||||
}
|
||||
|
||||
ASIntegerMap *result = [[ASIntegerMap alloc] init];
|
||||
// Start with the old indexes
|
||||
NSMutableIndexSet *indexes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, oldCount)];
|
||||
|
||||
// Descending order, shift deleted ranges left
|
||||
[deletions enumerateRangesWithOptions:NSEnumerationReverse usingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||
[indexes shiftIndexesStartingAtIndex:NSMaxRange(range) by:-range.length];
|
||||
}];
|
||||
|
||||
// Ascending order, shift inserted ranges right
|
||||
[insertions enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||
[indexes shiftIndexesStartingAtIndex:range.location by:range.length];
|
||||
}];
|
||||
|
||||
__block NSInteger oldIndex = 0;
|
||||
[indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||
// Note we advance oldIndex unconditionally, not newIndex
|
||||
for (NSInteger newIndex = range.location; newIndex < NSMaxRange(range); oldIndex++) {
|
||||
if ([deletions containsIndex:oldIndex]) {
|
||||
// index was deleted, do nothing, just let oldIndex advance.
|
||||
} else {
|
||||
// assign the next index for this item.
|
||||
result->_map[oldIndex] = newIndex++;
|
||||
}
|
||||
}
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSInteger)integerForKey:(NSInteger)key
|
||||
{
|
||||
if (_isIdentity) {
|
||||
return key;
|
||||
} else if (_isEmpty) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
auto result = _map.find(key);
|
||||
return result != _map.end() ? result->second : NSNotFound;
|
||||
}
|
||||
|
||||
- (void)setInteger:(NSInteger)value forKey:(NSInteger)key
|
||||
{
|
||||
if (_immutable) {
|
||||
ASDisplayNodeFailAssert(@"Cannot mutate special integer map: %@", self);
|
||||
return;
|
||||
}
|
||||
|
||||
_map[key] = value;
|
||||
}
|
||||
|
||||
- (ASIntegerMap *)inverseMap
|
||||
{
|
||||
if (_isIdentity || _isEmpty) {
|
||||
return self;
|
||||
}
|
||||
|
||||
auto result = [[ASIntegerMap alloc] init];
|
||||
for (auto it = _map.begin(); it != _map.end(); it++) {
|
||||
result->_map[it->second] = it->first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
if (_immutable) {
|
||||
return self;
|
||||
}
|
||||
|
||||
auto newMap = [[ASIntegerMap allocWithZone:zone] init];
|
||||
newMap->_map = _map;
|
||||
return newMap;
|
||||
}
|
||||
|
||||
#pragma mark - Description
|
||||
|
||||
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
|
||||
{
|
||||
NSMutableArray *result = [NSMutableArray array];
|
||||
|
||||
if (_isIdentity) {
|
||||
[result addObject:@{ @"map": @"<identity>" }];
|
||||
} else if (_isEmpty) {
|
||||
[result addObject:@{ @"map": @"<empty>" }];
|
||||
} else {
|
||||
// { 1->2 3->4 5->6 }
|
||||
NSMutableString *str = [NSMutableString string];
|
||||
for (auto it = _map.begin(); it != _map.end(); it++) {
|
||||
[str appendFormat:@" %zd->%zd", it->first, it->second];
|
||||
}
|
||||
// Remove leading space
|
||||
if (str.length > 0) {
|
||||
[str deleteCharactersInRange:NSMakeRange(0, 1)];
|
||||
}
|
||||
[result addObject:@{ @"map": str }];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return ASObjectDescriptionMakeWithoutObject([self propertiesForDescription]);
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if ([super isEqual:object]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (auto otherMap = ASDynamicCast(object, ASIntegerMap)) {
|
||||
return otherMap->_map == _map;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -18,6 +18,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
#import <AsyncDisplayKit/ASElementMap.h>
|
||||
#import <AsyncDisplayKit/ASIntegerMap.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -57,7 +58,7 @@ AS_SUBCLASSING_RESTRICTED
|
||||
*
|
||||
* This also deletes any supplementary elements in deleted sections.
|
||||
*/
|
||||
- (void)migrateSupplementaryElementsWithChangeSet:(_ASHierarchyChangeSet *)changeSet;
|
||||
- (void)migrateSupplementaryElementsWithSectionMapping:(ASIntegerMap *)mapping;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// ASMutableElementMap.mm
|
||||
// ASMutableElementMap.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
@@ -22,7 +22,6 @@
|
||||
#import <AsyncDisplayKit/ASElementMap.h>
|
||||
#import <AsyncDisplayKit/ASTwoDimensionalArrayUtils.h>
|
||||
#import <AsyncDisplayKit/NSIndexSet+ASHelpers.h>
|
||||
#import <AsyncDisplayKit/_ASHierarchyChangeSet.h>
|
||||
|
||||
typedef NSMutableArray<NSMutableArray<ASCollectionElement *> *> ASMutableCollectionElementTwoDimensionalArray;
|
||||
|
||||
@@ -102,9 +101,10 @@ typedef NSMutableDictionary<NSString *, NSMutableDictionary<NSIndexPath *, ASCol
|
||||
}
|
||||
}
|
||||
|
||||
- (void)migrateSupplementaryElementsWithChangeSet:(_ASHierarchyChangeSet *)changeSet
|
||||
- (void)migrateSupplementaryElementsWithSectionMapping:(ASIntegerMap *)mapping
|
||||
{
|
||||
if (changeSet.deletedSections.count == 0 && changeSet.insertedSections.count == 0) {
|
||||
// Fast-path, no section changes.
|
||||
if (mapping == ASIntegerMap.identityMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ typedef NSMutableDictionary<NSString *, NSMutableDictionary<NSIndexPath *, ASCol
|
||||
NSMutableDictionary *newSupps = [NSMutableDictionary dictionary];
|
||||
[supps enumerateKeysAndObjectsUsingBlock:^(NSIndexPath * _Nonnull oldIndexPath, ASCollectionElement * _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
NSInteger oldSection = oldIndexPath.section;
|
||||
NSInteger newSection = [changeSet newSectionForOldSection:oldSection];
|
||||
NSInteger newSection = [mapping integerForKey:oldSection];
|
||||
|
||||
if (oldSection == newSection) {
|
||||
// Index path stayed the same, just copy it over.
|
||||
@@ -18,6 +18,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <vector>
|
||||
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
||||
#import <AsyncDisplayKit/ASIntegerMap.h>
|
||||
#import <AsyncDisplayKit/ASLog.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -91,7 +92,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType);
|
||||
|
||||
@property (nonatomic, readonly) _ASHierarchyChangeType changeType;
|
||||
|
||||
+ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray<_ASHierarchyItemChange *> *)changes;
|
||||
+ (NSDictionary<NSNumber *, NSIndexSet *> *)sectionToIndexSetMapFromChanges:(NSArray<_ASHierarchyItemChange *> *)changes;
|
||||
|
||||
/**
|
||||
* If this is a .OriginalInsert or .OriginalDelete change, this returns a copied change
|
||||
@@ -154,6 +155,28 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType);
|
||||
*/
|
||||
- (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection;
|
||||
|
||||
/**
|
||||
* A table that maps old section indexes to new section indexes.
|
||||
*/
|
||||
@property (nonatomic, readonly, strong) ASIntegerMap *sectionMapping;
|
||||
|
||||
/**
|
||||
* A table that maps new section indexes to old section indexes.
|
||||
*/
|
||||
@property (nonatomic, readonly, strong) ASIntegerMap *reverseSectionMapping;
|
||||
|
||||
/**
|
||||
* A table that provides the item mapping for the old section. If the section was deleted
|
||||
* or is out of bounds, returns the empty table.
|
||||
*/
|
||||
- (ASIntegerMap *)itemMappingInSection:(NSInteger)oldSection;
|
||||
|
||||
/**
|
||||
* A table that provides the reverse item mapping for the new section. If the section was inserted
|
||||
* or is out of bounds, returns the empty table.
|
||||
*/
|
||||
- (ASIntegerMap *)reverseItemMappingInSection:(NSInteger)newSection;
|
||||
|
||||
/**
|
||||
* Get the old item index path for the given new index path.
|
||||
*
|
||||
@@ -162,6 +185,14 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType);
|
||||
*/
|
||||
- (nullable NSIndexPath *)oldIndexPathForNewIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Get the new item index path for the given old index path.
|
||||
*
|
||||
* @precondition The change set must be completed.
|
||||
* @return The new index path, or nil if the given item was deleted.
|
||||
*/
|
||||
- (nullable NSIndexPath *)newIndexPathForOldIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/// Call this once the change set has been constructed to prevent future modifications to the changeset. Calling this more than once is a programmer error.
|
||||
/// NOTE: Calling this method will cause the changeset to convert all reloads into delete/insert pairs.
|
||||
- (void)markCompletedWithNewItemCounts:(std::vector<NSInteger>)newItemCounts;
|
||||
|
||||
@@ -101,6 +101,12 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
|
||||
|
||||
@interface _ASHierarchyChangeSet ()
|
||||
|
||||
// array index is old section index, map goes oldItem -> newItem
|
||||
@property (nonatomic, strong, readonly) NSMutableArray<ASIntegerMap *> *itemMappings;
|
||||
|
||||
// array index is new section index, map goes newItem -> oldItem
|
||||
@property (nonatomic, strong, readonly) NSMutableArray<ASIntegerMap *> *reverseItemMappings;
|
||||
|
||||
@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchyItemChange *> *insertItemChanges;
|
||||
@property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchyItemChange *> *originalInsertItemChanges;
|
||||
|
||||
@@ -124,6 +130,10 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
|
||||
std::vector<NSInteger> _newItemCounts;
|
||||
void (^_completionHandler)(BOOL finished);
|
||||
}
|
||||
@synthesize sectionMapping = _sectionMapping;
|
||||
@synthesize reverseSectionMapping = _reverseSectionMapping;
|
||||
@synthesize itemMappings = _itemMappings;
|
||||
@synthesize reverseItemMappings = _reverseItemMappings;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
@@ -244,55 +254,121 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
|
||||
|
||||
- (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection
|
||||
{
|
||||
ASDisplayNodeAssertNotNil(_deletedSections, @"Cannot call %@ before `markCompleted` returns.", NSStringFromSelector(_cmd));
|
||||
ASDisplayNodeAssertNotNil(_insertedSections, @"Cannot call %@ before `markCompleted` returns.", NSStringFromSelector(_cmd));
|
||||
[self _ensureCompleted];
|
||||
if ([_deletedSections containsIndex:oldSection]) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
NSUInteger newIndex = oldSection - [_deletedSections countOfIndexesInRange:NSMakeRange(0, oldSection)];
|
||||
newIndex += [_insertedSections as_indexChangeByInsertingItemsBelowIndex:newIndex];
|
||||
return newIndex;
|
||||
return [self.sectionMapping integerForKey:oldSection];
|
||||
}
|
||||
|
||||
- (NSUInteger)oldSectionForNewSection:(NSUInteger)newSection
|
||||
{
|
||||
[self _ensureCompleted];
|
||||
if ([_insertedSections containsIndex:newSection]) {
|
||||
return NSNotFound;
|
||||
return [self.reverseSectionMapping integerForKey:newSection];
|
||||
}
|
||||
|
||||
NSInteger oldIndex = newSection - [_insertedSections as_indexChangeByInsertingItemsBelowIndex:newSection];
|
||||
oldIndex += [_deletedSections countOfIndexesInRange:NSMakeRange(0, oldIndex)];
|
||||
return oldIndex;
|
||||
- (ASIntegerMap *)sectionMapping
|
||||
{
|
||||
ASDisplayNodeAssertNotNil(_deletedSections, @"Cannot call %s before `markCompleted` returns.", sel_getName(_cmd));
|
||||
ASDisplayNodeAssertNotNil(_insertedSections, @"Cannot call %s before `markCompleted` returns.", sel_getName(_cmd));
|
||||
[self _ensureCompleted];
|
||||
if (_sectionMapping == nil) {
|
||||
_sectionMapping = [ASIntegerMap mapForUpdateWithOldCount:_oldItemCounts.size() deleted:_deletedSections inserted:_insertedSections];
|
||||
}
|
||||
return _sectionMapping;
|
||||
}
|
||||
|
||||
- (ASIntegerMap *)reverseSectionMapping
|
||||
{
|
||||
if (_reverseSectionMapping == nil) {
|
||||
_reverseSectionMapping = [self.sectionMapping inverseMap];
|
||||
}
|
||||
return _reverseSectionMapping;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)itemMappings
|
||||
{
|
||||
[self _ensureCompleted];
|
||||
|
||||
if (_itemMappings == nil) {
|
||||
_itemMappings = [NSMutableArray array];
|
||||
auto insertMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_originalInsertItemChanges];
|
||||
auto deleteMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_originalDeleteItemChanges];
|
||||
NSInteger oldSection = 0;
|
||||
for (auto oldCount : _oldItemCounts) {
|
||||
NSInteger newSection = [self newSectionForOldSection:oldSection];
|
||||
ASIntegerMap *table;
|
||||
if (newSection == NSNotFound) {
|
||||
table = ASIntegerMap.emptyMap;
|
||||
} else {
|
||||
table = [ASIntegerMap mapForUpdateWithOldCount:oldCount deleted:deleteMap[@(oldSection)] inserted:insertMap[@(newSection)]];
|
||||
}
|
||||
_itemMappings[oldSection] = table;
|
||||
oldSection++;
|
||||
}
|
||||
}
|
||||
return _itemMappings;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)reverseItemMappings
|
||||
{
|
||||
[self _ensureCompleted];
|
||||
|
||||
if (_reverseItemMappings == nil) {
|
||||
_reverseItemMappings = [NSMutableArray array];
|
||||
for (NSInteger newSection = 0; newSection < _newItemCounts.size(); newSection++) {
|
||||
NSInteger oldSection = [self oldSectionForNewSection:newSection];
|
||||
ASIntegerMap *table;
|
||||
if (oldSection == NSNotFound) {
|
||||
table = ASIntegerMap.emptyMap;
|
||||
} else {
|
||||
table = [[self itemMappingInSection:oldSection] inverseMap];
|
||||
}
|
||||
_reverseItemMappings[newSection] = table;
|
||||
}
|
||||
}
|
||||
return _reverseItemMappings;
|
||||
}
|
||||
|
||||
- (ASIntegerMap *)itemMappingInSection:(NSInteger)oldSection
|
||||
{
|
||||
if (self.includesReloadData || oldSection >= _oldItemCounts.size()) {
|
||||
return ASIntegerMap.emptyMap;
|
||||
}
|
||||
return self.itemMappings[oldSection];
|
||||
}
|
||||
|
||||
- (ASIntegerMap *)reverseItemMappingInSection:(NSInteger)newSection
|
||||
{
|
||||
if (self.includesReloadData || newSection >= _newItemCounts.size()) {
|
||||
return ASIntegerMap.emptyMap;
|
||||
}
|
||||
return self.reverseItemMappings[newSection];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)oldIndexPathForNewIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[self _ensureCompleted];
|
||||
// Inserted sections return nil.
|
||||
NSInteger newSection = indexPath.section;
|
||||
NSInteger newItem = indexPath.item;
|
||||
NSInteger oldSection = [self oldSectionForNewSection:newSection];
|
||||
if (oldSection == NSNotFound) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Inserted items return nil.
|
||||
for (_ASHierarchyItemChange *change in _originalInsertItemChanges) {
|
||||
if ([change.indexPaths containsObject:indexPath]) {
|
||||
NSInteger oldItem = [[self reverseItemMappingInSection:newSection] integerForKey:indexPath.item];
|
||||
if (oldItem == NSNotFound) {
|
||||
return nil;
|
||||
}
|
||||
return [NSIndexPath indexPathForItem:oldItem inSection:oldSection];
|
||||
}
|
||||
|
||||
// TODO: This is a pretty inefficient way to do this.
|
||||
NSIndexSet *insertsInSection = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_insertItemChanges][@(newSection)];
|
||||
NSIndexSet *deletesInSection = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_deleteItemChanges][@(oldSection)];
|
||||
|
||||
NSInteger oldIndex = newItem - [insertsInSection as_indexChangeByInsertingItemsBelowIndex:newItem];
|
||||
oldIndex += [deletesInSection countOfIndexesInRange:NSMakeRange(0, oldIndex)];
|
||||
return [NSIndexPath indexPathForItem:oldIndex inSection:oldSection];
|
||||
- (NSIndexPath *)newIndexPathForOldIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[self _ensureCompleted];
|
||||
NSInteger oldSection = indexPath.section;
|
||||
NSInteger newSection = [self newSectionForOldSection:oldSection];
|
||||
if (newSection == NSNotFound) {
|
||||
return nil;
|
||||
}
|
||||
NSInteger newItem = [[self itemMappingInSection:oldSection] integerForKey:indexPath.item];
|
||||
if (newItem == NSNotFound) {
|
||||
return nil;
|
||||
}
|
||||
return [NSIndexPath indexPathForItem:newItem inSection:newSection];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
@@ -424,34 +500,12 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
|
||||
}
|
||||
|
||||
[_ASHierarchyItemChange ensureItemChanges:_insertItemChanges ofSameType:_ASHierarchyChangeTypeInsert];
|
||||
NSDictionary *insertedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_insertItemChanges];
|
||||
|
||||
[_ASHierarchyItemChange ensureItemChanges:_deleteItemChanges ofSameType:_ASHierarchyChangeTypeDelete];
|
||||
NSDictionary *deletedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_deleteItemChanges];
|
||||
|
||||
for (_ASHierarchyItemChange *change in _reloadItemChanges) {
|
||||
NSAssert(change.changeType == _ASHierarchyChangeTypeReload, @"It must be a reload change to be in here");
|
||||
NSMutableArray *newIndexPaths = [NSMutableArray arrayWithCapacity:change.indexPaths.count];
|
||||
|
||||
// Every indexPaths in the change need to update its section and/or row
|
||||
// depending on all the deletions and insertions
|
||||
// For reference, when batching reloads/deletes/inserts:
|
||||
// - delete/reload indexPaths that are passed in should all be their current indexPaths
|
||||
// - insert indexPaths that are passed in should all be their future indexPaths after deletions
|
||||
for (NSIndexPath *indexPath in change.indexPaths) {
|
||||
NSUInteger section = [self newSectionForOldSection:indexPath.section];
|
||||
NSUInteger item = indexPath.item;
|
||||
|
||||
// Update row number based on deletions that are above the current row in the current section
|
||||
NSIndexSet *indicesDeletedInSection = deletedIndexPathsMap[@(indexPath.section)];
|
||||
item -= [indicesDeletedInSection countOfIndexesInRange:NSMakeRange(0, item)];
|
||||
// Update row number based on insertions that are above the current row in the future section
|
||||
NSIndexSet *indicesInsertedInSection = insertedIndexPathsMap[@(section)];
|
||||
item += [indicesInsertedInSection as_indexChangeByInsertingItemsBelowIndex:item];
|
||||
|
||||
NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:item inSection:section];
|
||||
[newIndexPaths addObject:newIndexPath];
|
||||
}
|
||||
auto newIndexPaths = ASArrayByFlatMapping(change.indexPaths, NSIndexPath *indexPath, [self newIndexPathForOldIndexPath:indexPath]);
|
||||
|
||||
// All reload changes are translated into deletes and inserts
|
||||
// We delete the items that needs reload together with other deleted items, at their original index
|
||||
|
||||
117
Tests/ASIntegerMapTests.m
Normal file
117
Tests/ASIntegerMapTests.m
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// ASIntegerMapTests.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "ASTestCase.h"
|
||||
#import "ASIntegerMap.h"
|
||||
|
||||
@interface ASIntegerMapTests : ASTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASIntegerMapTests
|
||||
|
||||
- (void)testIsEqual
|
||||
{
|
||||
ASIntegerMap *map = [[ASIntegerMap alloc] init];
|
||||
[map setInteger:1 forKey:0];
|
||||
ASIntegerMap *alsoMap = [[ASIntegerMap alloc] init];
|
||||
[alsoMap setInteger:1 forKey:0];
|
||||
ASIntegerMap *notMap = [[ASIntegerMap alloc] init];
|
||||
[notMap setInteger:2 forKey:0];
|
||||
XCTAssertEqualObjects(map, alsoMap);
|
||||
XCTAssertNotEqualObjects(map, notMap);
|
||||
}
|
||||
|
||||
#pragma mark - Changeset mapping
|
||||
|
||||
/// 1 item, no changes -> identity map
|
||||
- (void)testEmptyChange
|
||||
{
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:1 deleted:nil inserted:nil];
|
||||
XCTAssertEqual(map, ASIntegerMap.identityMap);
|
||||
}
|
||||
|
||||
/// 0 items -> empty map
|
||||
- (void)testChangeOnNoData
|
||||
{
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:0 deleted:nil inserted:nil];
|
||||
XCTAssertEqual(map, ASIntegerMap.emptyMap);
|
||||
}
|
||||
|
||||
/// 2 items, delete 0
|
||||
- (void)testBasicChange1
|
||||
{
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:2 deleted:[NSIndexSet indexSetWithIndex:0] inserted:nil];
|
||||
XCTAssertEqual([map integerForKey:0], NSNotFound);
|
||||
XCTAssertEqual([map integerForKey:1], 0);
|
||||
XCTAssertEqual([map integerForKey:2], NSNotFound);
|
||||
}
|
||||
|
||||
/// 2 items, insert 0
|
||||
- (void)testBasicChange2
|
||||
{
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:2 deleted:nil inserted:[NSIndexSet indexSetWithIndex:0]];
|
||||
XCTAssertEqual([map integerForKey:0], 1);
|
||||
XCTAssertEqual([map integerForKey:1], 2);
|
||||
XCTAssertEqual([map integerForKey:2], NSNotFound);
|
||||
}
|
||||
|
||||
/// 2 items, insert 0, delete 0
|
||||
- (void)testChange1
|
||||
{
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:2 deleted:[NSIndexSet indexSetWithIndex:0] inserted:[NSIndexSet indexSetWithIndex:0]];
|
||||
XCTAssertEqual([map integerForKey:0], NSNotFound);
|
||||
XCTAssertEqual([map integerForKey:1], 1);
|
||||
XCTAssertEqual([map integerForKey:2], NSNotFound);
|
||||
}
|
||||
|
||||
/// 4 items, insert {0-1, 3}
|
||||
- (void)testChange2
|
||||
{
|
||||
NSMutableIndexSet *inserts = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
|
||||
[inserts addIndex:3];
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:4 deleted:nil inserted:inserts];
|
||||
XCTAssertEqual([map integerForKey:0], 2);
|
||||
XCTAssertEqual([map integerForKey:1], 4);
|
||||
XCTAssertEqual([map integerForKey:2], 5);
|
||||
XCTAssertEqual([map integerForKey:3], 6);
|
||||
}
|
||||
|
||||
/// 4 items, delete {0-1, 3}
|
||||
- (void)testChange3
|
||||
{
|
||||
NSMutableIndexSet *deletes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
|
||||
[deletes addIndex:3];
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:4 deleted:deletes inserted:nil];
|
||||
XCTAssertEqual([map integerForKey:0], NSNotFound);
|
||||
XCTAssertEqual([map integerForKey:1], NSNotFound);
|
||||
XCTAssertEqual([map integerForKey:2], 0);
|
||||
XCTAssertEqual([map integerForKey:3], NSNotFound);
|
||||
}
|
||||
|
||||
/// 5 items, delete {0-1, 3} insert {1-2, 4}
|
||||
- (void)testChange4
|
||||
{
|
||||
NSMutableIndexSet *deletes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
|
||||
[deletes addIndex:3];
|
||||
NSMutableIndexSet *inserts = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)];
|
||||
[inserts addIndex:4];
|
||||
ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:5 deleted:deletes inserted:inserts];
|
||||
XCTAssertEqual([map integerForKey:0], NSNotFound);
|
||||
XCTAssertEqual([map integerForKey:1], NSNotFound);
|
||||
XCTAssertEqual([map integerForKey:2], 0);
|
||||
XCTAssertEqual([map integerForKey:3], NSNotFound);
|
||||
XCTAssertEqual([map integerForKey:4], 3);
|
||||
XCTAssertEqual([map integerForKey:5], NSNotFound);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -75,7 +75,7 @@ static __weak ASTestCase *currentTestCase;
|
||||
|
||||
// Go ahead and spin the run loop before finishing, so the system
|
||||
// unregisters/cleans up whatever possible.
|
||||
[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture];
|
||||
[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantPast];
|
||||
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user