Skip to content

Commit

Permalink
Introduce ASIntegerMap, improve our changeset handling
Browse files Browse the repository at this point in the history
Rename to ASIntegerMap

License header
  • Loading branch information
Adlai Holler committed Jul 5, 2017
1 parent 4a1aea2 commit 1f994f2
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 67 deletions.
16 changes: 12 additions & 4 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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, ); }; };
Expand All @@ -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 */; };
Expand Down Expand Up @@ -788,7 +790,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>"; };
Expand All @@ -807,6 +809,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>"; };
Expand Down Expand Up @@ -1210,6 +1214,8 @@
058D09E1195D050800B7D73C /* Details */ = {
isa = PBXGroup;
children = (
CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */,
CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */,
CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */,
CC0F885D1E4280B800576FED /* _ASCollectionViewCell.m */,
3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */,
Expand Down Expand Up @@ -1318,7 +1324,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 */,
Expand Down Expand Up @@ -1713,6 +1719,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 */,
Expand Down Expand Up @@ -2210,7 +2217,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 */,
Expand Down Expand Up @@ -2279,6 +2286,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 */,
Expand Down
2 changes: 1 addition & 1 deletion Source/Details/ASDataController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ - (void)_updateElementsInMap:(ASMutableElementMap *)map
}

// 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];
Expand Down
58 changes: 58 additions & 0 deletions Source/Details/ASIntegerMap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// 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 an 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:(NSIndexSet *)deleted
inserted:(NSIndexSet *)inserted;

/**
* A singleton that maps each integer to itself. Its inverse is itself.
*/
@property (class, atomic, readonly) ASIntegerMap *identityMap;

/**
* A singleton that returns NSNotFound for all keys. Its inverse is itself.
*/
@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;

/**
* Create and return a map with the inverse mapping.
*/
- (ASIntegerMap *)inverseMap;

@end

NS_ASSUME_NONNULL_END
156 changes: 156 additions & 0 deletions Source/Details/ASIntegerMap.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//
// 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 <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;
}

#pragma mark - Singleton

+ (ASIntegerMap *)identityMap
{
static ASIntegerMap *identityMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
identityMap = [[ASIntegerMap alloc] init];
identityMap->_isIdentity = YES;
});
return identityMap;
}

+ (ASIntegerMap *)emptyMap
{
static ASIntegerMap *emptyMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
emptyMap = [[ASIntegerMap alloc] init];
emptyMap->_isEmpty = 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;
}

- (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
{
return self;
}

#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]);
}

@end
3 changes: 2 additions & 1 deletion Source/Private/ASMutableElementMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASElementMap.h>
#import <AsyncDisplayKit/ASIntegerMap.h>

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// ASMutableElementMap.mm
// ASMutableElementMap.m
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
Expand All @@ -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;

Expand Down Expand Up @@ -102,9 +101,10 @@ - (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)
}
}

- (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;
}

Expand All @@ -118,7 +118,7 @@ - (void)migrateSupplementaryElementsWithChangeSet:(_ASHierarchyChangeSet *)chang
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.
Expand Down
Loading

0 comments on commit 1f994f2

Please sign in to comment.