Skip to content

Commit 403a3ab

Browse files
authored
Merge pull request ra1028#38 from ra1028/v0.7.0
v0.7.0
2 parents d22741e + ff1cd54 commit 403a3ab

30 files changed

+781
-342
lines changed

.swift-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.1
1+
4.2

.travis.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,5 @@ matrix:
1212
- xcodebuild build-for-testing test-without-building -scheme DifferenceKit -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8' ENABLE_TESTABILITY=YES | xcpretty - c
1313
- xcodebuild build-for-testing test-without-building -scheme DifferenceKit -configuration Release -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' ENABLE_TESTABILITY=YES | xcpretty -c
1414
- xcodebuild build -scheme DifferenceKit -configuration Release -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch - 38mm' ENABLE_TESTABILITY=YES | xcpretty -c
15-
- os: osx
16-
language: objective-c
17-
osx_image: xcode9.4
18-
script:
19-
- xcodebuild build -scheme DifferenceKit -configuration Release ENABLE_TESTABILITY=YES | xcpretty -c
20-
- xcodebuild build -scheme DifferenceKit -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8' | xcpretty -c
2115
notifications:
22-
email: false
16+
email: false

DifferenceKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |spec|
22
spec.name = 'DifferenceKit'
3-
spec.version = '0.6.0'
3+
spec.version = '0.7.0'
44
spec.author = { 'ra1028' => 'r.fe51028.r@gmail.com' }
55
spec.homepage = 'https://github.com/ra1028/DifferenceKit'
66
spec.documentation_url = 'https://ra1028.github.io/DifferenceKit'

DifferenceKit.xcodeproj/project.pbxproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
/* Begin PBXBuildFile section */
1010
6B2DF878210E2C12004D2D40 /* DifferenceKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B2DF86E210E2C12004D2D40 /* DifferenceKit.framework */; };
11+
6B444B392163312700AEE32B /* ContentEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B444B382163312700AEE32B /* ContentEquatable.swift */; };
1112
6B5B409C211066BF00A931DB /* AlgorithmTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B4093211066BF00A931DB /* AlgorithmTest.swift */; };
1213
6B5B409D211066BF00A931DB /* ArraySectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B4094211066BF00A931DB /* ArraySectionTest.swift */; };
1314
6B5B409E211066BF00A931DB /* StagedChangesetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B4095211066BF00A931DB /* StagedChangesetTest.swift */; };
@@ -42,6 +43,7 @@
4243
6B2DF86E210E2C12004D2D40 /* DifferenceKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DifferenceKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4344
6B2DF877210E2C12004D2D40 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4445
6B2DF88A210E39A8004D2D40 /* DifferenceKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DifferenceKit.xcconfig; sourceTree = "<group>"; };
46+
6B444B382163312700AEE32B /* ContentEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentEquatable.swift; sourceTree = "<group>"; };
4547
6B5B4086211066B300A931DB /* Algorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Algorithm.swift; sourceTree = "<group>"; };
4648
6B5B4088211066B300A931DB /* UIKitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitExtension.swift; sourceTree = "<group>"; };
4749
6B5B408A211066B300A931DB /* ArraySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArraySection.swift; sourceTree = "<group>"; };
@@ -117,6 +119,7 @@
117119
6B5B4086211066B300A931DB /* Algorithm.swift */,
118120
6B5B408F211066B300A931DB /* Changeset.swift */,
119121
6B5B408C211066B300A931DB /* StagedChangeset.swift */,
122+
6B444B382163312700AEE32B /* ContentEquatable.swift */,
120123
6B5B4091211066B300A931DB /* Differentiable.swift */,
121124
6B5B408E211066B300A931DB /* DifferentiableSection.swift */,
122125
6B5B408B211066B300A931DB /* AnyDifferentiable.swift */,
@@ -261,6 +264,7 @@
261264
isa = PBXSourcesBuildPhase;
262265
buildActionMask = 2147483647;
263266
files = (
267+
6B444B392163312700AEE32B /* ContentEquatable.swift in Sources */,
264268
6B5B40A8211066EA00A931DB /* Changeset.swift in Sources */,
265269
6B5B40AB211066EA00A931DB /* AnyDifferentiable.swift in Sources */,
266270
755D649621514ECB0049A3C5 /* AppKitExtension.swift in Sources */,
@@ -358,7 +362,7 @@
358362
SDKROOT = iphoneos;
359363
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
360364
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
361-
SWIFT_VERSION = 4.0;
365+
SWIFT_VERSION = 4.2;
362366
VERSIONING_SYSTEM = "apple-generic";
363367
VERSION_INFO_PREFIX = "";
364368
};
@@ -415,7 +419,7 @@
415419
SDKROOT = iphoneos;
416420
SWIFT_COMPILATION_MODE = wholemodule;
417421
SWIFT_OPTIMIZATION_LEVEL = "-O";
418-
SWIFT_VERSION = 4.0;
422+
SWIFT_VERSION = 4.2;
419423
VALIDATE_PRODUCT = YES;
420424
VERSIONING_SYSTEM = "apple-generic";
421425
VERSION_INFO_PREFIX = "";

README.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,14 @@ In the case of definition above, `id` uniquely identifies the element and get to
8787

8888
There are default implementations of `Differentiable` for the types that conformed to `Equatable` or `Hashable`
8989
```swift
90-
public extension Differentiable where Self: Equatable {
91-
func isContentEqual(to source: Self) -> Bool {
92-
return self == source
93-
}
90+
// If `Self` conform to `Hashable`.
91+
var differenceIdentifier: Self {
92+
return self
9493
}
9594

96-
public extension Differentiable where Self: Hashable {
97-
var differenceIdentifier: Self {
98-
return self
99-
}
95+
// If `Self` conform to `Equatable`.
96+
func isContentEqual(to source: Self) -> Bool {
97+
return self == source
10098
}
10199
```
102100
So, you can simply:
@@ -170,7 +168,7 @@ collectionView.reload(using: changeset, interrupt: { $0.changeCount > 100 }) { d
170168

171169
## Comparison with Other Frameworks
172170
Made a fair comparison as much as possible in features and performance with other **popular** and **awesome** frameworks.
173-
⚠️ This does `NOT` determine superiority or inferiority of the frameworks. I know that each framework has different benefits.
171+
This does **NOT** determine superiority or inferiority of the frameworks. I know that each framework has different benefits.
174172
The frameworks and its version that compared is below.
175173

176174
- [DifferenceKit](https://github.com/ra1028/DifferenceKit) - master
@@ -232,7 +230,7 @@ Use `Foundation.UUID` as an element.
232230
#### - From 5,000 elements to 500 deleted and 500 inserted
233231
| |Time(second)|
234232
|:------------|:-----------|
235-
|DifferenceKit|0.0032 |
233+
|DifferenceKit|0.0022 |
236234
|RxDataSources|0.0078 |
237235
|FlexibleDiff |0.0168 |
238236
|IGListKit |0.0412 |
@@ -244,7 +242,7 @@ Use `Foundation.UUID` as an element.
244242
#### - From 10,000 elements to 1,000 deleted and 1,000 inserted
245243
| |Time(second)|
246244
|:------------|:-----------|
247-
|DifferenceKit|0.0076 |
245+
|DifferenceKit|0.0049 |
248246
|RxDataSources|0.0143 |
249247
|FlexibleDiff |0.0305 |
250248
|IGListKit |0.0891 |
@@ -256,7 +254,7 @@ Use `Foundation.UUID` as an element.
256254
#### - From 100,000 elements to 10,000 deleted and 10,000 inserted
257255
| |Time(second)|
258256
|:------------|:-----------|
259-
|DifferenceKit|0.087 |
257+
|DifferenceKit|0.057 |
260258
|RxDataSources|0.179 |
261259
|FlexibleDiff |0.356 |
262260
|IGListKit |1.329 |
@@ -268,7 +266,7 @@ Use `Foundation.UUID` as an element.
268266
---
269267

270268
## Requirements
271-
- Swift4.1+
269+
- Swift4.2+
272270
- iOS 9.0+
273271
- tvOS 9.0+
274272
- OS X 10.9+

Sources/Algorithm.swift

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public extension StagedChangeset where Collection: RangeReplaceableCollection, C
1818
/// - target: A target collection to calculate differences.
1919
///
2020
/// - Complexity: O(n)
21+
@inlinable
2122
public init(source: Collection, target: Collection) {
2223
self.init(source: source, target: target, section: 0)
2324
}
@@ -42,6 +43,7 @@ public extension StagedChangeset where Collection: RangeReplaceableCollection, C
4243
/// - section: An Int value to use as section index (or offset) of element.
4344
///
4445
/// - Complexity: O(n)
46+
@inlinable
4547
public init(source: Collection, target: Collection, section: Int) {
4648
let sourceElements = ContiguousArray(source)
4749
let targetElements = ContiguousArray(target)
@@ -134,6 +136,7 @@ public extension StagedChangeset where Collection: RangeReplaceableCollection, C
134136
/// - target: A target sectioned collection to calculate differences.
135137
///
136138
/// - Complexity: O(n)
139+
@inlinable
137140
public init(source: Collection, target: Collection) {
138141
typealias Section = Collection.Element
139142
typealias SectionIdentifier = Collection.Element.DifferenceIdentifier
@@ -405,8 +408,9 @@ public extension StagedChangeset where Collection: RangeReplaceableCollection, C
405408
}
406409

407410
/// The shared algorithm to calculate differences between two linear collections.
411+
@inlinable
408412
@discardableResult
409-
private func differentiate<E: Differentiable, I>(
413+
internal func differentiate<E: Differentiable, I>(
410414
source: ContiguousArray<E>,
411415
target: ContiguousArray<E>,
412416
trackTargetIndexAsUpdated: Bool,
@@ -528,16 +532,23 @@ private func differentiate<E: Differentiable, I>(
528532
}
529533

530534
/// A set of changes and metadata as a result of calculating differences in linear collection.
531-
private struct DifferentiateResult<Index> {
532-
typealias Metadata = (sourceTraces: ContiguousArray<Trace<Int>>, targetReferences: ContiguousArray<Int?>)
533-
534-
let deleted: [Index]
535-
let inserted: [Index]
536-
let updated: [Index]
537-
let moved: [(source: Index, target: Index)]
538-
let metadata: Metadata
539-
540-
init(
535+
@usableFromInline
536+
internal struct DifferentiateResult<Index> {
537+
@usableFromInline
538+
internal typealias Metadata = (sourceTraces: ContiguousArray<Trace<Int>>, targetReferences: ContiguousArray<Int?>)
539+
@usableFromInline
540+
internal let deleted: [Index]
541+
@usableFromInline
542+
internal let inserted: [Index]
543+
@usableFromInline
544+
internal let updated: [Index]
545+
@usableFromInline
546+
internal let moved: [(source: Index, target: Index)]
547+
@usableFromInline
548+
internal let metadata: Metadata
549+
550+
@inlinable
551+
internal init(
541552
deleted: [Index] = [],
542553
inserted: [Index] = [],
543554
updated: [Index] = [],
@@ -553,32 +564,46 @@ private struct DifferentiateResult<Index> {
553564
}
554565

555566
/// A set of informations in middle of difference calculation.
556-
private struct Trace<Index> {
557-
var reference: Index?
558-
var deleteOffset = 0
559-
var isTracked = false
567+
@usableFromInline
568+
internal struct Trace<Index> {
569+
@usableFromInline
570+
internal var reference: Index?
571+
@usableFromInline
572+
internal var deleteOffset = 0
573+
@usableFromInline
574+
internal var isTracked = false
575+
576+
@inlinable
577+
init() {}
560578
}
561579

562580
/// The occurrences of element.
563-
private enum Occurrence {
581+
@usableFromInline
582+
internal enum Occurrence {
564583
case unique(index: Int)
565584
case duplicate(reference: IndicesReference)
566585
}
567586

568587
/// A mutable reference to indices of elements.
569-
private final class IndicesReference {
570-
private var indices: ContiguousArray<Int>
571-
private var position = 0
572-
573-
init(_ indices: ContiguousArray<Int>) {
588+
@usableFromInline
589+
internal final class IndicesReference {
590+
@usableFromInline
591+
internal var indices: ContiguousArray<Int>
592+
@usableFromInline
593+
internal var position = 0
594+
595+
@inlinable
596+
internal init(_ indices: ContiguousArray<Int>) {
574597
self.indices = indices
575598
}
576599

577-
func push(_ index: Int) {
600+
@inlinable
601+
internal func push(_ index: Int) {
578602
indices.append(index)
579603
}
580604

581-
func next() -> Int? {
605+
@inlinable
606+
internal func next() -> Int? {
582607
guard position < indices.endIndex else {
583608
return nil
584609
}
@@ -588,23 +613,29 @@ private final class IndicesReference {
588613
}
589614

590615
/// Dictionary key using UnsafePointer for performance optimization.
591-
private struct TableKey<T: Hashable>: Hashable {
592-
let hashValue: Int
593-
private let pointer: UnsafePointer<T>
594-
595-
init(pointer: UnsafePointer<T>) {
616+
@usableFromInline
617+
internal struct TableKey<T: Hashable>: Hashable {
618+
@usableFromInline
619+
internal let hashValue: Int
620+
@usableFromInline
621+
internal let pointer: UnsafePointer<T>
622+
623+
@inlinable
624+
internal init(pointer: UnsafePointer<T>) {
596625
self.hashValue = pointer.pointee.hashValue
597626
self.pointer = pointer
598627
}
599628

600-
static func == (lhs: TableKey, rhs: TableKey) -> Bool {
629+
@inlinable
630+
internal static func == (lhs: TableKey, rhs: TableKey) -> Bool {
601631
return lhs.hashValue == rhs.hashValue
602632
&& (lhs.pointer.distance(to: rhs.pointer) == 0 || lhs.pointer.pointee == rhs.pointer.pointee)
603633
}
604634
}
605635

606-
private extension MutableCollection where Element: MutableCollection, Index == Int, Element.Index == Int {
607-
subscript(path: ElementPath) -> Element.Element {
636+
internal extension MutableCollection where Element: MutableCollection, Index == Int, Element.Index == Int {
637+
@inlinable
638+
internal subscript(path: ElementPath) -> Element.Element {
608639
get { return self[path.section][path.element] }
609640
set { self[path.section][path.element] = newValue }
610641
}

Sources/AnyDifferentiable.swift

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,27 @@
2424
/// print(changeset.isEmpty) // prints "false"
2525
public struct AnyDifferentiable: Differentiable {
2626
/// The value wrapped by this instance.
27-
public let base: Any
27+
@inlinable
28+
public var base: Any {
29+
return box.base
30+
}
31+
2832
/// A type-erased identifier value for difference calculation.
29-
public let differenceIdentifier: AnyHashable
33+
@inlinable
34+
public var differenceIdentifier: AnyHashable {
35+
return box.differenceIdentifier
36+
}
3037

31-
private let isContentEqualTo: (AnyDifferentiable) -> Bool
38+
@usableFromInline
39+
internal let box: AnyDifferentiableBox
3240

3341
/// Creates a type-erased differentiable value that wraps the given instance.
3442
///
3543
/// - Parameters:
3644
/// - base: A differentiable value to wrap.
45+
@inlinable
3746
public init<D: Differentiable>(_ base: D) {
38-
self.base = base
39-
self.differenceIdentifier = AnyHashable(base.differenceIdentifier)
40-
41-
self.isContentEqualTo = { source in
42-
guard let sourceBase = source.base as? D else { return false }
43-
return base.isContentEqual(to: sourceBase)
44-
}
47+
box = DifferentiableBox(base)
4548
}
4649

4750
/// Indicate whether the content of `base` is equals to the content of the given source value.
@@ -51,8 +54,9 @@ public struct AnyDifferentiable: Differentiable {
5154
///
5255
/// - Returns: A Boolean value indicating whether the content of `base` is equals
5356
/// to the content of `base` of the given source value.
57+
@inlinable
5458
public func isContentEqual(to source: AnyDifferentiable) -> Bool {
55-
return isContentEqualTo(source)
59+
return box.isContentEqual(to: source.box)
5660
}
5761
}
5862

@@ -61,3 +65,40 @@ extension AnyDifferentiable: CustomDebugStringConvertible {
6165
return "AnyDifferentiable(\(String(reflecting: base))"
6266
}
6367
}
68+
69+
@usableFromInline
70+
internal protocol AnyDifferentiableBox {
71+
var base: Any { get }
72+
var differenceIdentifier: AnyHashable { get }
73+
74+
func isContentEqual(to source: AnyDifferentiableBox) -> Bool
75+
}
76+
77+
@usableFromInline
78+
internal struct DifferentiableBox<Base: Differentiable>: AnyDifferentiableBox {
79+
@usableFromInline
80+
internal let baseComponent: Base
81+
82+
@inlinable
83+
internal var base: Any {
84+
return baseComponent
85+
}
86+
87+
@inlinable
88+
internal var differenceIdentifier: AnyHashable {
89+
return AnyHashable(baseComponent.differenceIdentifier)
90+
}
91+
92+
@inlinable
93+
internal init(_ base: Base) {
94+
baseComponent = base
95+
}
96+
97+
@inlinable
98+
internal func isContentEqual(to source: AnyDifferentiableBox) -> Bool {
99+
guard let sourceBase = source.base as? Base else {
100+
return false
101+
}
102+
return baseComponent.isContentEqual(to: sourceBase)
103+
}
104+
}

0 commit comments

Comments
 (0)