Skip to content

Commit f38e84c

Browse files
committed
Pass buffer to _projectReadOnly and fix appends
1 parent 90bd2a0 commit f38e84c

File tree

1 file changed

+70
-76
lines changed

1 file changed

+70
-76
lines changed

stdlib/public/core/KeyPath.swift

Lines changed: 70 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,15 @@ public class KeyPath<Root, Value>: PartialKeyPath<Root> {
352352
var isBreak = false
353353
let (rawComponent, _) = buffer.next()
354354

355-
return rawComponent._projectReadOnly(
356-
root,
357-
to: Value.self,
358-
endingWith: Value.self,
359-
&isBreak
360-
)
355+
return Builtin.emplace {
356+
rawComponent._projectReadOnly(
357+
root,
358+
to: Value.self,
359+
endingWith: Value.self,
360+
&isBreak,
361+
pointer: UnsafeMutablePointer<Value>($0)
362+
)
363+
}
361364
}
362365

363366
let maxSize = buffer.maxSize
@@ -385,37 +388,28 @@ public class KeyPath<Root, Value>: PartialKeyPath<Root> {
385388

386389
func projectCurrent<Current>(_: Current.Type) {
387390
func projectNew<New>(_: New.Type) {
388-
let newBase = currentValueBuffer.withMemoryRebound(
391+
let base = currentValueBuffer.withMemoryRebound(
389392
to: Current.self
390393
) {
391-
return rawComponent._projectReadOnly(
392-
$0.moveElement(from: 0),
394+
$0.moveElement(from: 0)
395+
}
396+
397+
currentValueBuffer.withMemoryRebound(to: New.self) {
398+
rawComponent._projectReadOnly(
399+
base,
393400
to: New.self,
394401
endingWith: Value.self,
395-
&isBreak
402+
&isBreak,
403+
pointer: $0.baseAddress._unsafelyUnwrappedUnchecked
396404
)
397405
}
398406

399407
// If we've broken from the projection, it means we found nil
400408
// while optional chaining.
401409
guard _fastPath(!isBreak) else {
402-
var value: Value = Builtin.zeroInitializer()
403-
404-
// Optional.none has a tag of 1
405-
let tag: UInt32 = 1
406-
Builtin.injectEnumTag(&value, tag._value)
407-
408-
currentValueBuffer.withMemoryRebound(to: Value.self) {
409-
$0.initializeElement(at: 0, to: value)
410-
}
411-
412410
return
413411
}
414412

415-
currentValueBuffer.withMemoryRebound(to: New.self) {
416-
$0.initializeElement(at: 0, to: newBase)
417-
}
418-
419413
currentType = newType
420414

421415
if isLast {
@@ -564,25 +558,26 @@ public class ReferenceWritableKeyPath<
564558
func projectCurrent<Current>(_: Current.Type) {
565559
var isBreak = false
566560

567-
let newBase = currentValueBuffer.withMemoryRebound(
561+
let base = currentValueBuffer.withMemoryRebound(
568562
to: Current.self
569563
) {
570-
return rawComponent._projectReadOnly(
571-
$0.moveElement(from: 0),
564+
$0.moveElement(from: 0)
565+
}
566+
567+
currentValueBuffer.withMemoryRebound(to: New.self) {
568+
rawComponent._projectReadOnly(
569+
base,
572570
to: New.self,
573571
endingWith: Value.self,
574-
&isBreak
572+
&isBreak,
573+
pointer: $0.baseAddress._unsafelyUnwrappedUnchecked
575574
)
576575
}
577576

578577
guard _fastPath(!isBreak) else {
579578
_preconditionFailure("should not have stopped key path projection")
580579
}
581580

582-
currentValueBuffer.withMemoryRebound(to: New.self) {
583-
$0.initializeElement(at: 0, to: newBase)
584-
}
585-
586581
currentType = nextType
587582
}
588583

@@ -1781,41 +1776,23 @@ internal struct RawKeyPathComponent {
17811776
count: buffer.count - componentSize)
17821777
}
17831778

1784-
internal enum ProjectionResult<NewValue, LeafValue> {
1785-
/// Continue projecting the key path with the given new value.
1786-
case `continue`(NewValue)
1787-
/// Stop projecting the key path and use the given value as the final
1788-
/// result of the projection.
1789-
case `break`(LeafValue)
1790-
1791-
internal var assumingContinue: NewValue {
1792-
switch self {
1793-
case .continue(let x):
1794-
return x
1795-
case .break:
1796-
_internalInvariantFailure("should not have stopped key path projection")
1797-
}
1798-
}
1799-
}
1800-
18011779
internal func _projectReadOnly<CurValue, NewValue, LeafValue>(
18021780
_ base: CurValue,
18031781
to: NewValue.Type,
18041782
endingWith: LeafValue.Type,
1805-
_ isBreak: inout Bool
1806-
) -> NewValue {
1783+
_ isBreak: inout Bool,
1784+
pointer: UnsafeMutablePointer<NewValue>
1785+
) {
18071786
switch value {
18081787
case .struct(let offset):
1809-
let newValue = _withUnprotectedUnsafeBytes(of: base) {
1788+
_withUnprotectedUnsafeBytes(of: base) {
18101789
let p = $0.baseAddress._unsafelyUnwrappedUnchecked + offset
18111790

18121791
// The contents of the struct should be well-typed, so we can assume
18131792
// typed memory here.
1814-
return p.assumingMemoryBound(to: NewValue.self).pointee
1793+
pointer.initialize(to: p.assumingMemoryBound(to: NewValue.self).pointee)
18151794
}
18161795

1817-
return newValue
1818-
18191796
case .class(let offset):
18201797
_internalInvariant(CurValue.self is AnyObject.Type,
18211798
"base is not a class")
@@ -1830,21 +1807,24 @@ internal struct RawKeyPathComponent {
18301807
// 'modify' access.
18311808
Builtin.performInstantaneousReadAccess(offsetAddress._rawValue,
18321809
NewValue.self)
1833-
return offsetAddress.assumingMemoryBound(to: NewValue.self).pointee
1810+
1811+
pointer.initialize(
1812+
to: offsetAddress.assumingMemoryBound(to: NewValue.self).pointee
1813+
)
18341814

18351815
case .get(id: _, accessors: let accessors, argument: let argument),
18361816
.mutatingGetSet(id: _, accessors: let accessors, argument: let argument),
18371817
.nonmutatingGetSet(id: _, accessors: let accessors, argument: let argument):
18381818
let getter: ComputedAccessorsPtr.Getter<CurValue, NewValue> = accessors.getter()
18391819

1840-
let newValue = getter(
1841-
base,
1842-
argument?.data.baseAddress ?? accessors._value,
1843-
argument?.data.count ?? 0
1820+
pointer.initialize(
1821+
to: getter(
1822+
base,
1823+
argument?.data.baseAddress ?? accessors._value,
1824+
argument?.data.count ?? 0
1825+
)
18441826
)
18451827

1846-
return newValue
1847-
18481828
case .optionalChain:
18491829
_internalInvariant(CurValue.self == Optional<NewValue>.self,
18501830
"should be unwrapping optional value")
@@ -1857,17 +1837,21 @@ internal struct RawKeyPathComponent {
18571837
if _fastPath(tag == 0) {
18581838
// Optional "shares" a layout with its Wrapped type meaning we can
18591839
// reinterpret the base address as an address to its Wrapped value.
1860-
return Builtin.reinterpretCast(base)
1840+
pointer.initialize(to: Builtin.reinterpretCast(base))
1841+
return
18611842
}
18621843

18631844
// We found nil.
18641845
isBreak = true
18651846

1866-
// Return some zeroed out value for NewValue if we break. The caller will
1867-
// handle returning nil. We do this to prevent allocating metadata in this
1868-
// function because returning something like 'NewValue?' would need to
1869-
// allocate the optional metadata for 'NewValue'.
1870-
return Builtin.zeroInitializer()
1847+
// Initialize the leaf optional value by simply injecting the tag (which
1848+
// we've found to be 1) directly.
1849+
pointer.withMemoryRebound(to: LeafValue.self, capacity: 1) {
1850+
Builtin.injectEnumTag(
1851+
&$0.pointee,
1852+
tag._value
1853+
)
1854+
}
18711855

18721856
case .optionalForce:
18731857
_internalInvariant(CurValue.self == Optional<NewValue>.self,
@@ -1879,7 +1863,8 @@ internal struct RawKeyPathComponent {
18791863
if _fastPath(tag == 0) {
18801864
// Optional "shares" a layout with its Wrapped type meaning we can
18811865
// reinterpret the base address as an address to its Wrapped value.
1882-
return Builtin.reinterpretCast(base)
1866+
pointer.initialize(to: Builtin.reinterpretCast(base))
1867+
return
18831868
}
18841869

18851870
_preconditionFailure("unwrapped nil optional")
@@ -1893,7 +1878,7 @@ internal struct RawKeyPathComponent {
18931878
let tag: UInt32 = 0
18941879
Builtin.injectEnumTag(&new, tag._value)
18951880

1896-
return new
1881+
pointer.initialize(to: new)
18971882
}
18981883
}
18991884

@@ -2657,12 +2642,19 @@ internal func _appendingKeyPaths<
26572642
// header, plus space for the middle type.
26582643
// Align up the root so that we can put the component type after it.
26592644
let rootSize = MemoryLayout<Int>._roundingUpToAlignment(rootBuffer.data.count)
2660-
var resultSize = rootSize + leafBuffer.data.count
2661-
+ 2 * MemoryLayout<Int>.size
2645+
var resultSize = rootSize + // Root component size
2646+
leafBuffer.data.count + // Leaf component size
2647+
MemoryLayout<Int>.size // Middle type
2648+
2649+
// Size of just our components is equal to root + leaf + middle
26622650
let componentSize = resultSize
2663-
// The first tail allocated member is the maxSize of the keypath.
2651+
2652+
resultSize += MemoryLayout<Int>.size // Header size (padding if needed)
2653+
2654+
// The first member after the components is the maxSize of the keypath.
26642655
resultSize = MemoryLayout<Int>._roundingUpToAlignment(resultSize)
26652656
resultSize += MemoryLayout<Int>.size
2657+
26662658
// Immediately following is the tail-allocated space for the KVC string.
26672659
let totalResultSize = MemoryLayout<Int32>
26682660
._roundingUpToAlignment(resultSize + appendedKVCLength)
@@ -2686,12 +2678,14 @@ internal func _appendingKeyPaths<
26862678
// Save space for the header.
26872679
let leafIsReferenceWritable = type(of: leaf).kind == .reference
26882680
destBuilder.pushHeader(KeyPathBuffer.Header(
2689-
size: componentSize - MemoryLayout<Int>.size,
2681+
size: componentSize,
26902682
trivial: rootBuffer.trivial && leafBuffer.trivial,
26912683
hasReferencePrefix: rootBuffer.hasReferencePrefix
26922684
|| leafIsReferenceWritable,
2693-
isSingleComponent: rootBuffer.isSingleComponent !=
2694-
leafBuffer.isSingleComponent
2685+
2686+
// We've already checked if either is an identity, so both have at
2687+
// least 1 component.
2688+
isSingleComponent: false
26952689
))
26962690

26972691
let leafHasReferencePrefix = leafBuffer.hasReferencePrefix

0 commit comments

Comments
 (0)