diff --git a/Sources/OneWay/PropertyWrappers/CopyOnWrite.swift b/Sources/OneWay/PropertyWrappers/CopyOnWrite.swift new file mode 100644 index 0000000..fb761ec --- /dev/null +++ b/Sources/OneWay/PropertyWrappers/CopyOnWrite.swift @@ -0,0 +1,67 @@ +// +// OneWay +// The MIT License (MIT) +// +// Copyright (c) 2022-2024 SeungYeop Yeom ( https://github.com/DevYeom ). +// + +/// A property wrapper that allows for an easy way to eliminate the cost of copying large values +/// adopt copy-on-write behavior. +/// +/// When applied to a field, the corresponding value will always be heap-allocated. This happens +/// because this wrapper is a class and classes are always heap-allocated. Use of this wrapper is +/// required on large value types because they can overflow the Swift runtime stack. +/// +/// - SeeAlso: [Advice: Use copy-on-write semantics for large values](https://github.com/apple/swift/blob/main/docs/OptimizationTips.rst#advice-use-copy-on-write-semantics-for-large-values) +@propertyWrapper +public struct CopyOnWrite { + fileprivate final class Reference { + var value: Value + + init(_ value: Value) { + self.value = value + } + } + + public var wrappedValue: Value { + get { + reference.value + } + set { + if isKnownUniquelyReferenced(&reference) { + reference.value = newValue + } else { + reference = Reference(newValue) + } + } + } + + private var reference: Reference + + public init(wrappedValue: Value) { + reference = Reference(wrappedValue) + } +} + +extension CopyOnWrite: CustomStringConvertible { + public var description: String { + String(describing: reference.value) + } +} + +extension CopyOnWrite: Sendable where Value: Sendable { } +extension CopyOnWrite: Equatable where Value: Equatable { + public static func == (lhs: CopyOnWrite, rhs: CopyOnWrite) -> Bool { + lhs.wrappedValue == rhs.wrappedValue + } +} +extension CopyOnWrite: Hashable where Value : Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(wrappedValue) + } +} + +extension CopyOnWrite.Reference: @unchecked Sendable where Value: Sendable { } + +@available(*, deprecated, renamed: "CopyOnWrite") +public typealias Heap = CopyOnWrite diff --git a/Sources/OneWay/PropertyWrappers/Heap.swift b/Sources/OneWay/PropertyWrappers/Heap.swift deleted file mode 100644 index e03756b..0000000 --- a/Sources/OneWay/PropertyWrappers/Heap.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// OneWay -// The MIT License (MIT) -// -// Copyright (c) 2022-2024 SeungYeop Yeom ( https://github.com/DevYeom ). -// - -/// A property wrapper that allows for an easy way to implement storing data in heap memory. -/// -/// When applied to a field, the corresponding value will always be heap-allocated. This happens -/// because this wrapper is a class and classes are always heap-allocated. Use of this wrapper is -/// required on large value types because they can overflow the Swift runtime stack. -/// -/// - SeeAlso: [`CopyOnWrite.swift` located in `square/wire` repository](https://github.com/square/wire/blob/master/wire-runtime-swift/src/main/swift/propertyWrappers/CopyOnWrite.swift) -@propertyWrapper -public struct Heap { - fileprivate final class Storage { - var value: Value - - init(_ value: Value) { - self.value = value - } - } - - public var wrappedValue: Value { - get { - storage.value - } - - set { - if isKnownUniquelyReferenced(&storage) { - storage.value = newValue - } else { - storage = Storage(newValue) - } - } - } - - private var storage: Storage - - public init(wrappedValue: Value) { - storage = Storage(wrappedValue) - } -} - -extension Heap: CustomStringConvertible { - public var description: String { - String(describing: storage.value) - } -} - -extension Heap: Sendable where Value: Sendable { } -extension Heap: Equatable where Value: Equatable { - public static func == (lhs: Heap, rhs: Heap) -> Bool { - lhs.wrappedValue == rhs.wrappedValue - } -} -extension Heap: Hashable where Value : Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(wrappedValue) - } -} - -extension Heap.Storage: @unchecked Sendable where Value: Sendable { } diff --git a/Tests/OneWayTests/PropertyWrappersTests.swift b/Tests/OneWayTests/PropertyWrappersTests.swift index 0795e75..108609c 100644 --- a/Tests/OneWayTests/PropertyWrappersTests.swift +++ b/Tests/OneWayTests/PropertyWrappersTests.swift @@ -13,10 +13,10 @@ final class PropertyWrappersTests: XCTestCase { super.setUp() } - func test_heap() { + func test_copyOnWrite() { struct Storage { - @Heap var value: Int - @Heap var optionalValue: Int? + @CopyOnWrite var value: Int + @CopyOnWrite var optionalValue: Int? } do {