Skip to content

Commit dc8e9ce

Browse files
authored
Add PreferenceList implementation (#19)
* Add merge32 implementation * Update PreferenceNode * Complete PreferenceList implementation * Add HostPreferenceKey
1 parent 9783793 commit dc8e9ce

File tree

5 files changed

+230
-7
lines changed

5 files changed

+230
-7
lines changed

Sources/OpenSwiftUI/DataAndStorage/Preferences/Internal/HostPreferenceKey.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@
77
// Status: Complete
88

99
protocol HostPreferenceKey: PreferenceKey {}
10+
11+
extension HostPreferenceKey {
12+
static var _isReadableByHost: Bool { true }
13+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// HostPreferencesKey.swift
3+
// OpenSwiftUI
4+
//
5+
// Created by Kyle on 2023/1/6.
6+
// Lastest Version: iOS 15.5
7+
// Status: WIP
8+
// ID: 7429200566949B8FB892A77E01A988C8
9+
10+
struct HostPreferencesKey: PreferenceKey {
11+
static var defaultValue: PreferenceList {
12+
PreferenceList()
13+
}
14+
15+
static func reduce(value: inout PreferenceList, nextValue: () -> PreferenceList) {
16+
// TODO:
17+
}
18+
19+
private static var nodeId: UInt32 = .zero
20+
}

Sources/OpenSwiftUI/DataAndStorage/Preferences/Internal/PreferenceList.swift

Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,76 @@
44
//
55
// Created by Kyle on 2023/1/5.
66
// Lastest Version: iOS 15.5
7-
// Status: WIP
7+
// Status: Complete
8+
// ID: C1C63C2F6F2B9F3EB30DD747F0605FBD
89

9-
struct PreferenceList {
10+
struct PreferenceList: CustomStringConvertible {
1011
private var first: PreferenceNode?
11-
12+
1213
subscript<Key: PreferenceKey>(_ keyType: Key.Type) -> Value<Key.Value> {
13-
get { fatalError("TODO") }
14-
set { fatalError("TODO") }
14+
get {
15+
guard let first,
16+
let node = first.find(key: keyType) else {
17+
return Value(value: keyType.defaultValue, seed: .zero)
18+
}
19+
return Value(value: node.value, seed: node.seed)
20+
}
21+
set {
22+
if let first,
23+
let _ = first.find(key: keyType) {
24+
removeValue(for: keyType)
25+
}
26+
first = _PreferenceNode<Key>(value: newValue.value, seed: newValue.seed, next: first)
27+
}
28+
}
29+
30+
mutating func removeValue<Key: PreferenceKey>(for keyType: Key.Type) {
31+
let first = first
32+
self.first = nil
33+
first?.forEach { node in
34+
guard node.keyType != keyType else {
35+
return
36+
}
37+
self.first = node.copy(next: self.first)
38+
}
39+
}
40+
41+
func valueIfPresent<Key: PreferenceKey>(_ keyType: Key.Type) -> Value<Key.Value>? {
42+
guard let first else {
43+
return nil
44+
}
45+
return first.find(key: keyType).map { node in
46+
Value(value: node.value, seed: node.seed)
47+
}
48+
}
49+
50+
func contains<Key: PreferenceKey>(_ keyType: Key.Type) -> Bool {
51+
first?.find(key: keyType) != nil
52+
}
53+
54+
mutating func modifyValue<Key: PreferenceKey>(for keyType: Key.Type, transform: Value < (inout Key.Value) -> Void>) {
55+
var value = self[keyType]
56+
value.seed = value.seed.merge(transform.seed)
57+
transform.value(&value.value)
58+
removeValue(for: keyType)
59+
first = _PreferenceNode<Key>(value: value.value, seed: value.seed, next: first)
60+
}
61+
62+
var description: String {
63+
var description = "\((first?.mergedSeed ?? .zero).description): ["
64+
var currentNode = first
65+
var shouldAddSeparator = false
66+
while let node = currentNode {
67+
if shouldAddSeparator {
68+
description.append(", ")
69+
} else {
70+
shouldAddSeparator = true
71+
}
72+
description.append(node.description)
73+
currentNode = node.next
74+
}
75+
description.append("]")
76+
return description
1577
}
1678
}
1779

@@ -22,13 +84,93 @@ extension PreferenceList {
2284
}
2385
}
2486

25-
private class PreferenceNode {
87+
private class PreferenceNode: CustomStringConvertible {
2688
let keyType: Any.Type
2789
let seed: VersionSeed
2890
let mergedSeed: VersionSeed
2991
let next: PreferenceNode?
3092

3193
init(keyType: Any.Type, seed: VersionSeed, next: PreferenceNode?) {
32-
fatalError("TODO")
94+
self.keyType = keyType
95+
self.seed = seed
96+
let seedResult: VersionSeed = if let next {
97+
next.mergedSeed.merge(seed)
98+
} else {
99+
seed
100+
}
101+
self.mergedSeed = seedResult
102+
self.next = next
103+
}
104+
105+
final func forEach(_ body: (PreferenceNode) -> Void) {
106+
var node = self
107+
repeat {
108+
body(node)
109+
if let next = node.next {
110+
node = next
111+
} else {
112+
break
113+
}
114+
} while true
115+
}
116+
117+
final func find<Key: PreferenceKey>(key: Key.Type) -> _PreferenceNode<Key>? {
118+
var node = self
119+
repeat {
120+
if node.keyType == key {
121+
return node as? _PreferenceNode<Key>
122+
} else {
123+
if let next = node.next {
124+
node = next
125+
} else {
126+
break
127+
}
128+
}
129+
} while true
130+
return nil
131+
}
132+
133+
func find(from _: PreferenceNode?) -> PreferenceNode? { fatalError() }
134+
func combine(from _: PreferenceNode?, next _: PreferenceNode?) -> PreferenceNode? { fatalError() }
135+
func copy(next _: PreferenceNode?) -> PreferenceNode { fatalError() }
136+
var description: String { fatalError() }
137+
}
138+
139+
private class _PreferenceNode<Key: PreferenceKey>: PreferenceNode {
140+
let value: Key.Value
141+
142+
init(value: Key.Value, seed: VersionSeed, next: PreferenceNode?) {
143+
self.value = value
144+
super.init(keyType: Key.self, seed: seed, next: next)
145+
}
146+
147+
override func find(from: PreferenceNode?) -> PreferenceNode? {
148+
from?.find(key: Key.self)
149+
}
150+
151+
override func combine(from: PreferenceNode?, next: PreferenceNode?) -> PreferenceNode? {
152+
var currentNode = from
153+
while let node = currentNode {
154+
if keyType == node.keyType {
155+
var value = self.value
156+
var seed = self.seed
157+
Key.reduce(value: &value) {
158+
seed = seed.merge(node.seed)
159+
return (node as! _PreferenceNode).value
160+
}
161+
return _PreferenceNode(value: value, seed: seed, next: next)
162+
} else {
163+
currentNode = node.next
164+
}
165+
}
166+
return nil
167+
}
168+
169+
override func copy(next: PreferenceNode?) -> PreferenceNode {
170+
_PreferenceNode(value: value, seed: seed, next: next)
171+
}
172+
173+
override var description: String {
174+
"\(Key.self) = \(value)"
33175
}
34176
}

Sources/OpenSwiftUI/Internal/Graph/VersionSeed.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,34 @@ struct VersionSeed: CustomStringConvertible {
2222
static var invalid: VersionSeed { VersionSeed(value: .max) }
2323

2424
var isValid: Bool { value != VersionSeed.invalid.value }
25+
26+
@_transparent
27+
@inline(__always)
28+
func merge(_ seed: VersionSeed) -> VersionSeed {
29+
if isValid, seed.value == .zero {
30+
self
31+
} else if value == .zero, seed.isValid {
32+
seed
33+
} else {
34+
VersionSeed(value: merge32(value, seed.value))
35+
}
36+
}
37+
}
38+
39+
private func merge32(_ a: UInt32, _ b: UInt32) -> UInt32 {
40+
let a = UInt64(a)
41+
let b = UInt64(b)
42+
var c = b
43+
c &+= .max ^ (c &<< 32)
44+
c &+= a &<< 32
45+
c ^= (c &>> 22)
46+
c &+= .max ^ (c &<< 13)
47+
c ^= (c &>> 8)
48+
c &+= (c &<< 3)
49+
c ^= (c >> 15)
50+
c &+= .max ^ (c &<< 27)
51+
c ^= (c &>> 31)
52+
return UInt32(truncatingIfNeeded: c)
2553
}
2654

2755
struct VersionSeedTracker<Key: HostPreferenceKey> {

Tests/OpenSwiftUITests/Internal/VersionSeed/VersionSeedTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,33 @@ struct VersionSeedTests {
1818
let seed = VersionSeed(value: value)
1919
#expect(seed.description == expectedDescription)
2020
}
21+
22+
@Test(arguments: [
23+
(0x0000_0000, 0x0000_0000, 0x0000_0000),
24+
(0xFFFF_FFFF, 0x0000_0000, 0x17F8_3A02),
25+
(0xFFFF_FFFF, 0xFFFF_FFFF, 0x3258_2C16),
26+
(0xAABB_CCDD, 0x0000_0000, 0xAABB_CCDD),
27+
(0x0000_0000, 0xAABB_CCDD, 0xAABB_CCDD),
28+
(0xAABB_CCDD, 0xAABB_CCDD, 0x1AAD_F11C),
29+
(0xFFFF_FFFF, 0xAABB_CCDD, 0x4C96_0643),
30+
(0xAABB_CCDD, 0xFFFF_FFFF, 0x13DA_25CE),
31+
(0x0000_0001, 0x0001_0000, 0x8621_ACD2),
32+
(0x0001_0000, 0x0000_0001, 0xD5E2_C632),
33+
(0x1000_0000, 0x0000_0001, 0xE6E8_7354),
34+
(0x0000_0001, 0x1000_0000, 0xAE49_4475),
35+
])
36+
func merge(_ a: UInt32, _ b: UInt32, _ c: UInt32) {
37+
let seedA = VersionSeed(value: a)
38+
let seedB = VersionSeed(value: b)
39+
#expect(seedA.merge(seedB).value == c)
40+
}
41+
}
42+
43+
extension UInt32: CustomTestStringConvertible {
44+
public var testDescription: String { hex }
45+
private var hex: String {
46+
let high = UInt16(truncatingIfNeeded: self &>> 16)
47+
let low = UInt16(truncatingIfNeeded: self)
48+
return String(format: "0x%04X_%04X", high, low)
49+
}
2150
}

0 commit comments

Comments
 (0)