diff --git a/Sources/OpenSwiftUICore/Data/DynamicProperty/UnsafeHeterogeneousBuffer.swift b/Sources/OpenSwiftUICore/Data/DynamicProperty/UnsafeHeterogeneousBuffer.swift index cc43c6fd8..adfe111cf 100644 --- a/Sources/OpenSwiftUICore/Data/DynamicProperty/UnsafeHeterogeneousBuffer.swift +++ b/Sources/OpenSwiftUICore/Data/DynamicProperty/UnsafeHeterogeneousBuffer.swift @@ -50,19 +50,9 @@ package struct UnsafeHeterogeneousBuffer: Collection { } private mutating func allocate(_ bytes: Int) -> UnsafeMutableRawPointer { - var count = _count - var offset = 0 var size = 0 - while count != 0 { - let itemSize = buf - .advanced(by: offset) - .assumingMemoryBound(to: Item.self) - .pointee - .size - offset &+= Int(itemSize) - count &-= 1 - offset = count == 0 ? 0 : offset - size &+= Int(itemSize) + for element in self { + size += Int(element.item.pointee.size) } // Grow buffer if needed if Int(available) < bytes { @@ -108,7 +98,6 @@ package struct UnsafeHeterogeneousBuffer: Collection { oldBuffer += size newBuffer += size } while count != 0 || itemSize != 0 - } buf.deallocate() } @@ -118,18 +107,8 @@ package struct UnsafeHeterogeneousBuffer: Collection { package func destroy() { defer { buf?.deallocate() } - guard _count != 0 else { - return - } - var count = _count - var offset = 0 - while count != 0 { - let itemPointer = buf - .advanced(by: offset) - .assumingMemoryBound(to: Item.self) - itemPointer.pointee.vtable.deinitialize(elt: .init(item: itemPointer)) - offset &+= Int(itemPointer.pointee.size) - count &-= 1 + for element in self { + element.item.pointee.vtable.deinitialize(elt: element) } } diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift index e3a7d1994..e563b3c45 100644 --- a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift @@ -48,7 +48,7 @@ package final class ViewGraph: GraphHost { package weak var delegate: (any ViewGraphDelegate)? = nil - // private var features: ViewGraphFeaturesBuffer = .init() + private var features: ViewGraphFeatureBuffer = .init(contents: .init()) package var centersRootView: Bool = true @@ -183,15 +183,20 @@ package final class ViewGraph: GraphHost { deinit { // FIXME removePreferenceOutlets(isInvalidating: true) + features.contents.destroy() } override package var graphDelegate: GraphDelegate? { delegate } override package var parentHost: GraphHost? { preferenceBridge?.viewGraph } - // TODO: ViewGraphFeature - // package func append(feature: T) where T: ViewGraphFeature - // package subscript(feature: T.Type) -> UnsafeMutablePointer? where T: ViewGraphFeature + package func append(feature: T) where T: ViewGraphFeature { + features.append(feature) + } + + package subscript(feature: T.Type) -> UnsafeMutablePointer? where T: ViewGraphFeature { + features[feature] + } override package func instantiateOutputs() { #if canImport(Darwin) @@ -231,9 +236,13 @@ package final class ViewGraph: GraphHost { ) { rootGeometry in rootGeometry.$layoutDirection = inputs.mapEnvironment(\.layoutDirection) } - // TODO: features related - let outputs = makeRootView(rootView, inputs) - // TODO: features related + for feature in features { + feature.modifyViewInputs(inputs: &inputs, graph: self) + } + var outputs = makeRootView(rootView, inputs) + for feature in features { + feature.modifyViewOutputs(outputs: &outputs, inputs: inputs, graph: self) + } return outputs } $rootGeometry.mutateBody( @@ -263,7 +272,9 @@ package final class ViewGraph: GraphHost { override package func uninstantiateOutputs() { #if canImport(Darwin) removePreferenceOutlets(isInvalidating: false) - // TODO: features + for feature in features { + feature.uninstantiate(graph: self) + } $rootGeometry.mutateBody( as: RootGeometry.self, invalidating: true diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift index d3dc8c682..8ebcbd06c 100644 --- a/Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift @@ -4,6 +4,7 @@ // // Audited for iOS 18.0 // Status: Complete +// ID: 8A0FC0E1EA10CEEE185C2315B618A95C (SwiftUICore) package protocol ViewGraphFeature { mutating func modifyViewInputs(inputs: inout _ViewInputs, graph: ViewGraph) @@ -24,3 +25,129 @@ extension ViewGraphFeature { package mutating func needsUpdate(graph: ViewGraph) -> Bool { false } package mutating func update(graph: ViewGraph) {} } + +struct ViewGraphFeatureBuffer: Collection { + var contents: UnsafeHeterogeneousBuffer + + @discardableResult + mutating func append(_ feature: Feature) -> UnsafeHeterogeneousBuffer.Index where Feature: ViewGraphFeature { + contents.append(feature, vtable: _VTable.self) + } + + subscript(_ type: Feature.Type) -> UnsafeMutablePointer? where Feature: ViewGraphFeature { + guard !contents.isEmpty else { return nil } + for element in contents { + guard element.hasType(type) else { + continue + } + return element.body(as: type) + } + return nil + } + + typealias Index = UnsafeHeterogeneousBuffer.Index + + struct Element: ViewGraphFeature { + var base: UnsafeHeterogeneousBuffer.Element + + private var vtable: VTable.Type { + base.vtable(as: VTable.self) + } + + func modifyViewInputs(inputs: inout _ViewInputs, graph: ViewGraph) { + vtable.modifyViewInputs(elt: base, inputs: &inputs, graph: graph) + } + + func modifyViewOutputs(outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) { + vtable.modifyViewOutputs(elt: base, outputs: &outputs, inputs: inputs, graph: graph) + } + + func uninstantiate(graph: ViewGraph) { + vtable.uninstantiate(elt: base, graph: graph) + } + + func isHiddenForReuseDidChange(graph: ViewGraph) { + vtable.isHiddenForReuseDidChange(elt: base, graph: graph) + } + + func allowsAsyncUpdate(graph: ViewGraph) -> Bool? { + vtable.allowsAsyncUpdate(elt: base, graph: graph) + } + + func needsUpdate(graph: ViewGraph) -> Bool { + vtable.needsUpdate(elt: base, graph: graph) + } + + func update(graph: ViewGraph) { + vtable.update(elt: base, graph: graph) + } + } + + var startIndex: UnsafeHeterogeneousBuffer.Index { contents.startIndex } + + var endIndex: UnsafeHeterogeneousBuffer.Index { contents.endIndex } + + var isEmpty: Bool { contents.isEmpty } + + subscript(position: UnsafeHeterogeneousBuffer.Index) -> Element { + _read { yield Element(base: contents[position]) } + } + + func index(after i: UnsafeHeterogeneousBuffer.Index) -> UnsafeHeterogeneousBuffer.Index { + contents.index(after: i) + } + + private class VTable: _UnsafeHeterogeneousBuffer_VTable { + class func modifyViewInputs(elt: UnsafeHeterogeneousBuffer.Element, inputs: inout _ViewInputs, graph: ViewGraph) {} + class func modifyViewOutputs(elt: UnsafeHeterogeneousBuffer.Element, outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) {} + class func uninstantiate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {} + class func isHiddenForReuseDidChange(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {} + class func needsUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool { false } + class func allowsAsyncUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool? { nil } + class func update(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {} + } + + private final class _VTable: VTable where Feature: ViewGraphFeature { + override class func hasType(_ type: T.Type) -> Bool { + Feature.self == T.self + } + + override class func moveInitialize(elt: UnsafeHeterogeneousBuffer.Element, from: _UnsafeHeterogeneousBuffer_Element) { + let dest = elt.body(as: Feature.self) + let source = from.body(as: Feature.self) + dest.initialize(to: source.move()) + } + + override class func deinitialize(elt: UnsafeHeterogeneousBuffer.Element) { + elt.body(as: Feature.self).deinitialize(count: 1) + } + + override class func modifyViewInputs(elt: UnsafeHeterogeneousBuffer.Element, inputs: inout _ViewInputs, graph: ViewGraph) { + elt.body(as: Feature.self).pointee.modifyViewInputs(inputs: &inputs, graph: graph) + } + + override class func modifyViewOutputs(elt: UnsafeHeterogeneousBuffer.Element, outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) { + elt.body(as: Feature.self).pointee.modifyViewOutputs(outputs: &outputs, inputs: inputs, graph: graph) + } + + override class func uninstantiate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) { + elt.body(as: Feature.self).pointee.uninstantiate(graph: graph) + } + + override class func isHiddenForReuseDidChange(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) { + elt.body(as: Feature.self).pointee.isHiddenForReuseDidChange(graph: graph) + } + + override class func needsUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool { + elt.body(as: Feature.self).pointee.needsUpdate(graph: graph) + } + + override class func allowsAsyncUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool? { + elt.body(as: Feature.self).pointee.allowsAsyncUpdate(graph: graph) + } + + override class func update(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) { + elt.body(as: Feature.self).pointee.update(graph: graph) + } + } +}