diff --git a/Sources/JWSETKit/Base/ProtectedContainer.swift b/Sources/JWSETKit/Base/ProtectedContainer.swift index c0d4b7f..8c069e7 100644 --- a/Sources/JWSETKit/Base/ProtectedContainer.swift +++ b/Sources/JWSETKit/Base/ProtectedContainer.swift @@ -90,9 +90,7 @@ extension TypedProtectedWebContainer { } public subscript(dynamicMember keyPath: KeyPath) -> T { - get { - value[keyPath: keyPath] - } + value[keyPath: keyPath] } public subscript(dynamicMember keyPath: WritableKeyPath) -> T { diff --git a/Sources/JWSETKit/Cryptography/Algorithms/Algorithms.swift b/Sources/JWSETKit/Cryptography/Algorithms/Algorithms.swift index fd4dd5a..56a4abd 100644 --- a/Sources/JWSETKit/Cryptography/Algorithms/Algorithms.swift +++ b/Sources/JWSETKit/Cryptography/Algorithms/Algorithms.swift @@ -145,7 +145,7 @@ public struct JSONWebKeyCurve: StringRepresentable { } extension JSONWebKeyCurve { - private static let keySizes: ReadWriteLockedValue<[Self: Int]> = [ + private static let keySizes: PthreadReadWriteLockedValue<[Self: Int]> = [ .p256: 32, .ed25519: 32, .x25519: 32, .p384: 48, .p521: 66, diff --git a/Sources/JWSETKit/Cryptography/Algorithms/Compression.swift b/Sources/JWSETKit/Cryptography/Algorithms/Compression.swift index 30ab74f..8ea7dc3 100644 --- a/Sources/JWSETKit/Cryptography/Algorithms/Compression.swift +++ b/Sources/JWSETKit/Cryptography/Algorithms/Compression.swift @@ -45,11 +45,11 @@ public struct JSONWebCompressionAlgorithm: StringRepresentable { extension JSONWebCompressionAlgorithm { #if canImport(Compression) - private static let compressors: ReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = [ + private static let compressors: PthreadReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = [ .deflate: AppleCompressor.self, ] #else - private static let compressors: ReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = [:] + private static let compressors: PthreadReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = [:] #endif /// Returns provided compressor for this algorithm. diff --git a/Sources/JWSETKit/Cryptography/Algorithms/ContentEncryption.swift b/Sources/JWSETKit/Cryptography/Algorithms/ContentEncryption.swift index 59fb2d7..2a2fa27 100644 --- a/Sources/JWSETKit/Cryptography/Algorithms/ContentEncryption.swift +++ b/Sources/JWSETKit/Cryptography/Algorithms/ContentEncryption.swift @@ -22,7 +22,7 @@ public struct JSONWebContentEncryptionAlgorithm: JSONWebAlgorithm { } extension JSONWebContentEncryptionAlgorithm { - private static let keyRegistryClasses: ReadWriteLockedValue<[Self: any JSONWebSealingKey.Type]> = [ + private static let keyRegistryClasses: PthreadReadWriteLockedValue<[Self: any JSONWebSealingKey.Type]> = [ .aesEncryptionGCM128: JSONWebKeyAESGCM.self, .aesEncryptionGCM192: JSONWebKeyAESGCM.self, .aesEncryptionGCM256: JSONWebKeyAESGCM.self, @@ -31,7 +31,7 @@ extension JSONWebContentEncryptionAlgorithm { .aesEncryptionCBC256SHA512: JSONWebKeyAESCBCHMAC.self, ] - private static let keyLengths: ReadWriteLockedValue<[Self: SymmetricKeySize]> = [ + private static let keyLengths: PthreadReadWriteLockedValue<[Self: SymmetricKeySize]> = [ .aesEncryptionGCM128: .bits128, .aesEncryptionGCM192: .bits192, .aesEncryptionGCM256: .bits256, diff --git a/Sources/JWSETKit/Cryptography/Algorithms/KeyEncryption.swift b/Sources/JWSETKit/Cryptography/Algorithms/KeyEncryption.swift index 0de9b46..52703f4 100644 --- a/Sources/JWSETKit/Cryptography/Algorithms/KeyEncryption.swift +++ b/Sources/JWSETKit/Cryptography/Algorithms/KeyEncryption.swift @@ -36,7 +36,7 @@ extension JSONWebKeyEncryptionAlgorithm { _ cek: inout Data ) throws -> Void - private static let keyRegistryClasses: ReadWriteLockedValue < [Self: (public: any JSONWebEncryptingKey.Type, private: any JSONWebDecryptingKey.Type)]> = [ + private static let keyRegistryClasses: PthreadReadWriteLockedValue < [Self: (public: any JSONWebEncryptingKey.Type, private: any JSONWebDecryptingKey.Type)]> = [ .direct: (JSONWebDirectKey.self, JSONWebDirectKey.self), .aesKeyWrap128: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self), .aesKeyWrap192: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self), @@ -58,7 +58,7 @@ extension JSONWebKeyEncryptionAlgorithm { .ecdhEphemeralStaticAESKeyWrap256: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self), ] - private static let keyTypes: ReadWriteLockedValue<[Self: JSONWebKeyType]> = [ + private static let keyTypes: PthreadReadWriteLockedValue<[Self: JSONWebKeyType]> = [ .direct: .symmetric, .aesKeyWrap128: .symmetric, .aesKeyWrap192: .symmetric, @@ -80,7 +80,7 @@ extension JSONWebKeyEncryptionAlgorithm { .ecdhEphemeralStaticAESKeyWrap256: .symmetric, ] - private static let keyLengths: ReadWriteLockedValue<[Self: Int]> = [ + private static let keyLengths: PthreadReadWriteLockedValue<[Self: Int]> = [ .aesKeyWrap128: SymmetricKeySize.bits128.bitCount, .aesKeyWrap192: SymmetricKeySize.bits192.bitCount, .aesKeyWrap256: SymmetricKeySize.bits256.bitCount, @@ -100,7 +100,7 @@ extension JSONWebKeyEncryptionAlgorithm { .ecdhEphemeralStaticAESKeyWrap256: SymmetricKeySize.bits256.bitCount, ] - private static let hashFunctions: ReadWriteLockedValue<[Self: any HashFunction.Type]> = [ + private static let hashFunctions: PthreadReadWriteLockedValue<[Self: any HashFunction.Type]> = [ .aesKeyWrap128: SHA256.self, .aesKeyWrap192: SHA384.self, .aesKeyWrap256: SHA512.self, @@ -116,7 +116,7 @@ extension JSONWebKeyEncryptionAlgorithm { .ecdhEphemeralStaticAESKeyWrap256: SHA256.self, ] - private static let encryptedKeyHandlers: ReadWriteLockedValue<[Self: EncryptedKeyHandler]> = [ + private static let encryptedKeyHandlers: PthreadReadWriteLockedValue<[Self: EncryptedKeyHandler]> = [ .aesGCM128KeyWrap: aesGCMKeyWrapEncryptedKey, .aesGCM192KeyWrap: aesGCMKeyWrapEncryptedKey, .aesGCM256KeyWrap: aesGCMKeyWrapEncryptedKey, @@ -129,7 +129,7 @@ extension JSONWebKeyEncryptionAlgorithm { .ecdhEphemeralStaticAESKeyWrap256: ecdhEsEncryptedKey, ] - private static let decryptionMutators: ReadWriteLockedValue<[Self: DecryptionMutatorHandler]> = [ + private static let decryptionMutators: PthreadReadWriteLockedValue<[Self: DecryptionMutatorHandler]> = [ .direct: directDecryptionMutator, .aesGCM128KeyWrap: aesgcmDecryptionMutator, .aesGCM192KeyWrap: aesgcmDecryptionMutator, diff --git a/Sources/JWSETKit/Cryptography/Algorithms/Signature.swift b/Sources/JWSETKit/Cryptography/Algorithms/Signature.swift index cbcd708..10a8adc 100644 --- a/Sources/JWSETKit/Cryptography/Algorithms/Signature.swift +++ b/Sources/JWSETKit/Cryptography/Algorithms/Signature.swift @@ -22,7 +22,7 @@ public struct JSONWebSignatureAlgorithm: JSONWebAlgorithm { } extension JSONWebSignatureAlgorithm { - private static let keyRegistryClasses: ReadWriteLockedValue < [Self: (public: any JSONWebValidatingKey.Type, private: any JSONWebSigningKey.Type)]> = [ + private static let keyRegistryClasses: PthreadReadWriteLockedValue < [Self: (public: any JSONWebValidatingKey.Type, private: any JSONWebSigningKey.Type)]> = [ .none: (JSONWebDirectKey.self, JSONWebDirectKey.self), .hmacSHA256: (JSONWebKeyHMAC.self, JSONWebKeyHMAC.self), .hmacSHA384: (JSONWebKeyHMAC.self, JSONWebKeyHMAC.self), @@ -39,7 +39,7 @@ extension JSONWebSignatureAlgorithm { .rsaSignaturePKCS1v15SHA512: (JSONWebRSAPublicKey.self, JSONWebRSAPrivateKey.self), ] - private static let keyTypes: ReadWriteLockedValue<[Self: JSONWebKeyType]> = [ + private static let keyTypes: PthreadReadWriteLockedValue<[Self: JSONWebKeyType]> = [ .none: .empty, .hmacSHA256: .symmetric, .hmacSHA384: .symmetric, @@ -56,12 +56,12 @@ extension JSONWebSignatureAlgorithm { .rsaSignaturePKCS1v15SHA512: .rsa, ] - private static let curves: ReadWriteLockedValue<[Self: JSONWebKeyCurve]> = [ + private static let curves: PthreadReadWriteLockedValue<[Self: JSONWebKeyCurve]> = [ .ecdsaSignatureP256SHA256: .p256, .ecdsaSignatureP384SHA384: .p384, .ecdsaSignatureP521SHA512: .p521, .eddsaSignature: .ed25519, ] - private static let hashFunctions: ReadWriteLockedValue<[Self: any HashFunction.Type]> = [ + private static let hashFunctions: PthreadReadWriteLockedValue<[Self: any HashFunction.Type]> = [ .hmacSHA256: SHA256.self, .hmacSHA384: SHA384.self, .hmacSHA512: SHA512.self, diff --git a/Sources/JWSETKit/Cryptography/KeyParser.swift b/Sources/JWSETKit/Cryptography/KeyParser.swift index a737cc8..198567f 100644 --- a/Sources/JWSETKit/Cryptography/KeyParser.swift +++ b/Sources/JWSETKit/Cryptography/KeyParser.swift @@ -194,7 +194,7 @@ enum JSONWebKeyCertificateChainSpecializer: JSONWebKeySpecializer { } extension AnyJSONWebKey { - static let specializers: ReadWriteLockedValue<[any JSONWebKeySpecializer.Type]> = [ + static let specializers: PthreadReadWriteLockedValue<[any JSONWebKeySpecializer.Type]> = [ JSONWebKeyRSASpecializer.self, JSONWebKeyEllipticCurveSpecializer.self, JSONWebKeyCurve25519Specializer.self, diff --git a/Sources/JWSETKit/Extensions/Lock.swift b/Sources/JWSETKit/Extensions/Lock.swift deleted file mode 100644 index a49b5b6..0000000 --- a/Sources/JWSETKit/Extensions/Lock.swift +++ /dev/null @@ -1,316 +0,0 @@ -// -// Lock.swift -// -// -// Created by Amir Abbas Mousavian on 10/27/23. -// - -import Foundation - -final class ReadWriteLock: @unchecked Sendable { - private let lock: UnsafeMutablePointer - - init() { - self.lock = .allocate(capacity: 1) - lock.initialize(to: pthread_rwlock_t()) - pthread_rwlock_init(lock, nil) - } - - deinit { - pthread_rwlock_destroy(lock) - lock.deinitialize(count: 1) - lock.deallocate() - } - - enum LockType { - case read, write - } - - func lock(_ type: LockType) { - switch type { - case .read: - pthread_rwlock_rdlock(lock) - case .write: - pthread_rwlock_wrlock(lock) - } - } - - func unlock() { - pthread_rwlock_unlock(lock) - } - - @discardableResult - func withReadLock(_ handler: () throws -> R) rethrows -> R { - lock(.read) - defer { unlock() } - return try handler() - } - - @discardableResult - func withWriteLock(_ handler: () throws -> R) rethrows -> R { - lock(.write) - defer { unlock() } - return try handler() - } -} - -/// Synchronizing read and writes on a shared mutable property. -@dynamicMemberLookup -final class ReadWriteLockedValue: @unchecked Sendable { - private let lock = ReadWriteLock() - private var _value: T - - var wrappedValue: T { - get { - lock.withReadLock { _value } - } - set { - lock.withWriteLock { _value = newValue } - } - } - - init(wrappedValue: T) { - lock.lock(.write) - defer { lock.unlock() } - self._value = wrappedValue - } - - convenience init(_ wrappedValue: T) { - self.init(wrappedValue: wrappedValue) - } - - subscript(dynamicMember keyPath: KeyPath) -> U { - wrappedValue[keyPath: keyPath] - } - - subscript(dynamicMember keyPath: WritableKeyPath) -> U { - get { - wrappedValue[keyPath: keyPath] - } - set { - wrappedValue[keyPath: keyPath] = newValue - } - } -} - -extension ReadWriteLockedValue: Equatable where T: Equatable { - static func == (lhs: ReadWriteLockedValue, rhs: ReadWriteLockedValue) -> Bool { - lhs.wrappedValue == rhs.wrappedValue - } -} - -extension ReadWriteLockedValue: Comparable where T: Comparable { - static func < (lhs: ReadWriteLockedValue, rhs: ReadWriteLockedValue) -> Bool { - lhs.wrappedValue < rhs.wrappedValue - } -} - -extension ReadWriteLockedValue: Hashable where T: Hashable { - func hash(into hasher: inout Hasher) { - hasher.combine(wrappedValue) - } -} - -extension ReadWriteLockedValue: Sequence where T: Sequence { - func makeIterator() -> T.Iterator { - wrappedValue.makeIterator() - } - - var underestimatedCount: Int { - wrappedValue.underestimatedCount - } - - func withContiguousStorageIfAvailable(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? { - try wrappedValue.withContiguousStorageIfAvailable(body) - } -} - -extension ReadWriteLockedValue: Collection where T: Collection { - func index(after i: T.Index) -> T.Index { - wrappedValue.index(after: i) - } - - var startIndex: T.Index { - wrappedValue.startIndex - } - - var endIndex: T.Index { - wrappedValue.endIndex - } - - subscript(position: T.Index) -> T.Element { - wrappedValue[position] - } - - subscript(bounds: Range) -> T.SubSequence { - wrappedValue[bounds] - } -} - -extension ReadWriteLockedValue: MutableCollection where T: MutableCollection { - subscript(position: T.Index) -> T.Element { - get { - wrappedValue[position] - } - set { - wrappedValue[position] = newValue - } - } - - subscript(bounds: Range) -> T.SubSequence { - get { - wrappedValue[bounds] - } - set { - wrappedValue[bounds] = newValue - } - } -} - -extension ReadWriteLockedValue: RangeReplaceableCollection where T: RangeReplaceableCollection { - convenience init() { - self.init(wrappedValue: T()) - } - - convenience init(_ elements: S) where S : Sequence, T.Element == S.Element { - self.init(wrappedValue: T(elements)) - } - - convenience init(repeating repeatedValue: T.Element, count: Int) { - self.init(wrappedValue: T(repeating: repeatedValue, count: count)) - } - - func reserveCapacity(_ n: Int) { - wrappedValue.reserveCapacity(n) - } - - func replaceSubrange(_ subrange: Range, with newElements: C) where C : Collection, T.Element == C.Element { - wrappedValue.replaceSubrange(subrange, with: newElements) - } - - func append(_ newElement: T.Element) { - wrappedValue.append(newElement) - } - - func append(contentsOf newElements: S) where S : Sequence, T.Element == S.Element { - wrappedValue.append(contentsOf: newElements) - } - - func insert(_ newElement: T.Element, at i: T.Index) { - wrappedValue.insert(newElement, at: i) - } - - func insert(contentsOf newElements: S, at i: T.Index) where S : Collection, T.Element == S.Element { - wrappedValue.insert(contentsOf: newElements, at: i) - } - - func removeSubrange(_ bounds: Range) { - wrappedValue.removeSubrange(bounds) - } - - func remove(at i: T.Index) -> T.Element { - wrappedValue.remove(at: i) - } - - func removeFirst() -> T.Element { - wrappedValue.removeFirst() - } - - func removeFirst(_ k: Int) { - wrappedValue.removeFirst(k) - } - - func removeAll(where shouldBeRemoved: (T.Element) throws -> Bool) rethrows { - try wrappedValue.removeAll(where: shouldBeRemoved) - } - - func removeAll(keepingCapacity keepCapacity: Bool) { - wrappedValue.removeAll(keepingCapacity: keepCapacity) - } -} - -extension ReadWriteLockedValue: BidirectionalCollection where T: BidirectionalCollection { - func index(before i: T.Index) -> T.Index { - wrappedValue.index(before: i) - } -} - -extension ReadWriteLockedValue: RandomAccessCollection where T: RandomAccessCollection {} - -extension ReadWriteLockedValue: LazySequenceProtocol where T: LazySequenceProtocol {} - -extension ReadWriteLockedValue: LazyCollectionProtocol where T: LazyCollectionProtocol {} - -extension ReadWriteLockedValue: ExpressibleByNilLiteral where T: ExpressibleByNilLiteral { - convenience init(nilLiteral: ()) { - self.init(wrappedValue: nil) - } -} - -extension ReadWriteLockedValue: ExpressibleByArrayLiteral where T: ExpressibleByArrayLiteral & RangeReplaceableCollection { - convenience init(arrayLiteral elements: T.Element...) { - self.init(wrappedValue: T(elements)) - } -} - -extension ReadWriteLockedValue: ExpressibleByFloatLiteral where T: ExpressibleByFloatLiteral { - convenience init(floatLiteral value: T.FloatLiteralType) { - self.init(wrappedValue: T(floatLiteral: value)) - } -} - -extension ReadWriteLockedValue: ExpressibleByIntegerLiteral where T: ExpressibleByIntegerLiteral { - convenience init(integerLiteral value: T.IntegerLiteralType) { - self.init(wrappedValue: T(integerLiteral: value)) - } -} - -extension ReadWriteLockedValue: ExpressibleByUnicodeScalarLiteral where T: ExpressibleByUnicodeScalarLiteral { - convenience init(unicodeScalarLiteral value: T.UnicodeScalarLiteralType) { - self.init(wrappedValue: T(unicodeScalarLiteral: value)) - } -} - -extension ReadWriteLockedValue: ExpressibleByExtendedGraphemeClusterLiteral where T: ExpressibleByExtendedGraphemeClusterLiteral { - convenience init(extendedGraphemeClusterLiteral value: T.ExtendedGraphemeClusterLiteralType) { - self.init(wrappedValue: T(extendedGraphemeClusterLiteral: value)) - } -} - -extension ReadWriteLockedValue: ExpressibleByStringLiteral where T: ExpressibleByStringLiteral { - convenience init(stringLiteral value: T.StringLiteralType) { - self.init(wrappedValue: T(stringLiteral: value)) - } -} - -extension ReadWriteLockedValue: ExpressibleByStringInterpolation where T: ExpressibleByStringInterpolation { - convenience init(stringInterpolation: T.StringInterpolation) { - self.init(wrappedValue: T(stringInterpolation: stringInterpolation)) - } -} - -extension ReadWriteLockedValue: ExpressibleByBooleanLiteral where T: ExpressibleByBooleanLiteral { - convenience init(booleanLiteral value: T.BooleanLiteralType) { - self.init(wrappedValue: T(booleanLiteral: value)) - } -} - -protocol DictionaryInitialzable: ExpressibleByDictionaryLiteral { - init(elements: [(Key, Value)]) -} - -extension Dictionary: DictionaryInitialzable { - init(elements: [(Key, Value)]) { - self.init(uniqueKeysWithValues: elements) - } -} - -extension ReadWriteLockedValue: ExpressibleByDictionaryLiteral where T: ExpressibleByDictionaryLiteral & DictionaryInitialzable, T.Key: Hashable { - convenience init(dictionaryLiteral elements: (T.Key, T.Value)...) { - let elements = elements.map { ($0, $1) } - self.init(wrappedValue: T(elements: elements)) - } -} - -// Silence KeyPath concurrency-safe warning. -extension PartialKeyPath: @unchecked Sendable {} diff --git a/Sources/JWSETKit/Extensions/LockValue.swift b/Sources/JWSETKit/Extensions/LockValue.swift new file mode 100644 index 0000000..37a4f08 --- /dev/null +++ b/Sources/JWSETKit/Extensions/LockValue.swift @@ -0,0 +1,467 @@ +// +// LockValue.swift +// +// +// Created by Amir Abbas Mousavian on 4/29/24. +// + +import Foundation + +public protocol ReadWriteLockContext { + static var getContext: Self { get } + static var setContext: Self { get } +} + +public struct LockContextEmpty: ReadWriteLockContext { + public static var getContext: LockContextEmpty { + LockContextEmpty() + } + + public static var setContext: LockContextEmpty { + LockContextEmpty() + } +} + +public protocol Locking: Sendable { + associatedtype Context + + init() + func tryLock(_ context: Context) -> Bool + func lock(_ context: Context) throws + func unlock() +} + +extension Locking { + @inlinable + public func withLock(_ context: Context, _ handler: () throws -> R) throws -> R { + try lock(context) + defer { unlock() } + return try handler() + } +} + +public enum PthreadReadWriteContextLock: ReadWriteLockContext { + case read + case write + + @inlinable + public static var getContext: PthreadReadWriteContextLock { + .read + } + + @inlinable + public static var setContext: PthreadReadWriteContextLock { + .write + } +} + +public final class PthreadReadWriteLock: Locking, @unchecked Sendable { + private let lock: UnsafeMutablePointer + + public init() { + self.lock = .allocate(capacity: 1) + lock.initialize(to: pthread_rwlock_t()) + pthread_rwlock_init(lock, nil) + } + + deinit { + pthread_rwlock_destroy(lock) + lock.deinitialize(count: 1) + lock.deallocate() + } + + public func tryLock(_ context: PthreadReadWriteContextLock) -> Bool { + switch context { + case .read: + return pthread_rwlock_tryrdlock(lock) == 0 + case .write: + return pthread_rwlock_trywrlock(lock) == 0 + } + } + + public func lock(_ context: PthreadReadWriteContextLock) throws { + let result: Int32 + switch context { + case .read: + result = pthread_rwlock_rdlock(lock) + case .write: + result = pthread_rwlock_wrlock(lock) + } + if result != 0 { + throw POSIXError(POSIXError.Code(rawValue: result) ?? .ELAST) + } + } + + public func unlock() { + pthread_rwlock_unlock(lock) + } +} + +public typealias PthreadReadWriteLockedValue = LockedValue + +#if canImport(Darwin) +public final class OSUnfairLock: Locking, @unchecked Sendable { + private let lock: os_unfair_lock_t + + public init() { + self.lock = .allocate(capacity: 1) + lock.initialize(to: os_unfair_lock()) + } + + deinit { + lock.deinitialize(count: 1) + lock.deallocate() + } + + public func tryLock(_: LockContextEmpty) -> Bool { + os_unfair_lock_trylock(lock) + } + + public func lock(_: LockContextEmpty) { + os_unfair_lock_lock(lock) + } + + public func unlock() { + os_unfair_lock_unlock(lock) + } +} + +public typealias OSUnfairLockedValue = LockedValue +#endif + +public final class PthreadMutex: Locking, @unchecked Sendable { + private let lock: UnsafeMutablePointer + + public init() { + self.lock = .allocate(capacity: 1) + lock.initialize(to: pthread_mutex_t()) + pthread_mutex_init(lock, nil) + } + + deinit { + pthread_mutex_destroy(lock) + lock.deinitialize(count: 1) + lock.deallocate() + } + + public func tryLock(_: LockContextEmpty) -> Bool { + pthread_mutex_trylock(lock) == 0 + } + + public func lock(_: LockContextEmpty) { + pthread_mutex_lock(lock) + } + + public func unlock() { + pthread_mutex_unlock(lock) + } +} + +public typealias PthreadMutexLockedValue = LockedValue + +/// Synchronizing read and writes on a shared mutable property. +@dynamicMemberLookup +public final class LockedValue, Value>: @unchecked Sendable where Context: ReadWriteLockContext { + private let lock = Lock() + + private var _value: Value + + public var wrappedValue: Value { + get { + (try? lock.withLock(.getContext) { + _value + }) ?? _value + } + set { + try? lock.withLock(.setContext) { + _value = newValue + } + } + } + + public init(wrappedValue: Value) { + self._value = wrappedValue + } + + @inlinable + public subscript(dynamicMember keyPath: KeyPath) -> U { + wrappedValue[keyPath: keyPath] + } + + @inlinable + public subscript(dynamicMember keyPath: WritableKeyPath) -> U { + get { + wrappedValue[keyPath: keyPath] + } + set { + wrappedValue[keyPath: keyPath] = newValue + } + } + + public func withLock(_ context: Context, _ handler: (_ value: inout Value) throws -> R) throws -> R { + try lock.withLock(context) { + try handler(&_value) + } + } +} + +extension LockedValue: Equatable where Value: Equatable { + @inlinable + public static func == (lhs: LockedValue, rhs: LockedValue) -> Bool { + lhs.wrappedValue == rhs.wrappedValue + } +} + +extension LockedValue: Comparable where Value: Comparable { + @inlinable + public static func < (lhs: LockedValue, rhs: LockedValue) -> Bool { + lhs.wrappedValue < rhs.wrappedValue + } +} + +extension LockedValue: Hashable where Value: Hashable { + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(wrappedValue) + } +} + +extension LockedValue: Sequence where Value: Sequence { + @inlinable + public func makeIterator() -> Value.Iterator { + wrappedValue.makeIterator() + } + + @inlinable + public var underestimatedCounValue: Int { + wrappedValue.underestimatedCount + } + + @inlinable + public func withContiguousStorageIfAvailable(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? { + try wrappedValue.withContiguousStorageIfAvailable(body) + } +} + +extension LockedValue: Collection where Value: Collection { + @inlinable + public func index(after i: Value.Index) -> Value.Index { + wrappedValue.index(after: i) + } + + @inlinable + public var startIndex: Value.Index { + wrappedValue.startIndex + } + + @inlinable + public var endIndex: Value.Index { + wrappedValue.endIndex + } + + @inlinable + public subscript(position: Value.Index) -> Value.Element { + wrappedValue[position] + } + + @inlinable + public subscript(bounds: Range) -> Value.SubSequence { + wrappedValue[bounds] + } +} + +extension LockedValue: MutableCollection where Value: MutableCollection { + @inlinable + public subscript(position: Value.Index) -> Value.Element { + get { + wrappedValue[position] + } + set { + wrappedValue[position] = newValue + } + } + + @inlinable + public subscript(bounds: Range) -> Value.SubSequence { + get { + wrappedValue[bounds] + } + set { + wrappedValue[bounds] = newValue + } + } +} + +extension LockedValue: RangeReplaceableCollection where Value: RangeReplaceableCollection { + @inlinable + public convenience init() { + self.init(wrappedValue: Value()) + } + + @inlinable + public convenience init(_ elements: S) where S: Sequence, Value.Element == S.Element { + self.init(wrappedValue: Value(elements)) + } + + @inlinable + public convenience init(repeating repeatedValue: Value.Element, count: Int) { + self.init(wrappedValue: Value(repeating: repeatedValue, count: count)) + } + + @inlinable + public func reserveCapacity(_ n: Int) { + wrappedValue.reserveCapacity(n) + } + + @inlinable + public func replaceSubrange(_ subrange: Range, with newElements: C) where C: Collection, Value.Element == C.Element { + wrappedValue.replaceSubrange(subrange, with: newElements) + } + + @inlinable + public func append(_ newElement: Value.Element) { + wrappedValue.append(newElement) + } + + @inlinable + public func append(contentsOf newElements: S) where S: Sequence, Value.Element == S.Element { + wrappedValue.append(contentsOf: newElements) + } + + @inlinable + public func insert(_ newElement: Value.Element, at i: Value.Index) { + wrappedValue.insert(newElement, at: i) + } + + @inlinable + public func insert(contentsOf newElements: S, at i: Value.Index) where S: Collection, Value.Element == S.Element { + wrappedValue.insert(contentsOf: newElements, at: i) + } + + @inlinable + public func removeSubrange(_ bounds: Range) { + wrappedValue.removeSubrange(bounds) + } + + @inlinable + public func remove(at i: Value.Index) -> Value.Element { + wrappedValue.remove(at: i) + } + + @inlinable + public func removeFirst() -> Value.Element { + wrappedValue.removeFirst() + } + + @inlinable + public func removeFirst(_ k: Int) { + wrappedValue.removeFirst(k) + } + + @inlinable + public func removeAll(where shouldBeRemoved: (Value.Element) throws -> Bool) rethrows { + try wrappedValue.removeAll(where: shouldBeRemoved) + } + + @inlinable + public func removeAll(keepingCapacity keepCapacity: Bool) { + wrappedValue.removeAll(keepingCapacity: keepCapacity) + } +} + +extension LockedValue: BidirectionalCollection where Value: BidirectionalCollection { + @inlinable + public func index(before i: Value.Index) -> Value.Index { + wrappedValue.index(before: i) + } +} + +extension LockedValue: RandomAccessCollection where Value: RandomAccessCollection {} + +extension LockedValue: LazySequenceProtocol where Value: LazySequenceProtocol {} + +extension LockedValue: LazyCollectionProtocol where Value: LazyCollectionProtocol {} + +extension LockedValue: ExpressibleByNilLiteral where Value: ExpressibleByNilLiteral { + @inlinable + public convenience init(nilLiteral _: ()) { + self.init(wrappedValue: nil) + } +} + +extension LockedValue: ExpressibleByArrayLiteral where Value: ExpressibleByArrayLiteral & RangeReplaceableCollection { + @inlinable + public convenience init(arrayLiteral elements: Value.Element...) { + self.init(wrappedValue: Value(elements)) + } +} + +extension LockedValue: ExpressibleByFloatLiteral where Value: ExpressibleByFloatLiteral { + @inlinable + public convenience init(floatLiteral value: Value.FloatLiteralType) { + self.init(wrappedValue: Value(floatLiteral: value)) + } +} + +extension LockedValue: ExpressibleByIntegerLiteral where Value: ExpressibleByIntegerLiteral { + @inlinable + public convenience init(integerLiteral value: Value.IntegerLiteralType) { + self.init(wrappedValue: Value(integerLiteral: value)) + } +} + +extension LockedValue: ExpressibleByUnicodeScalarLiteral where Value: ExpressibleByUnicodeScalarLiteral { + @inlinable + public convenience init(unicodeScalarLiteral value: Value.UnicodeScalarLiteralType) { + self.init(wrappedValue: Value(unicodeScalarLiteral: value)) + } +} + +extension LockedValue: ExpressibleByExtendedGraphemeClusterLiteral where Value: ExpressibleByExtendedGraphemeClusterLiteral { + @inlinable + public convenience init(extendedGraphemeClusterLiteral value: Value.ExtendedGraphemeClusterLiteralType) { + self.init(wrappedValue: Value(extendedGraphemeClusterLiteral: value)) + } +} + +extension LockedValue: ExpressibleByStringLiteral where Value: ExpressibleByStringLiteral { + @inlinable + public convenience init(stringLiteral value: Value.StringLiteralType) { + self.init(wrappedValue: Value(stringLiteral: value)) + } +} + +extension LockedValue: ExpressibleByStringInterpolation where Value: ExpressibleByStringInterpolation { + @inlinable + public convenience init(stringInterpolation: Value.StringInterpolation) { + self.init(wrappedValue: Value(stringInterpolation: stringInterpolation)) + } +} + +extension LockedValue: ExpressibleByBooleanLiteral where Value: ExpressibleByBooleanLiteral { + @inlinable + public convenience init(booleanLiteral value: Value.BooleanLiteralType) { + self.init(wrappedValue: Value(booleanLiteral: value)) + } +} + +public protocol DictionaryInitialzable: ExpressibleByDictionaryLiteral { + init(elements: [(Key, Value)]) +} + +extension Dictionary: DictionaryInitialzable { + @inlinable + public init(elements: [(Key, Value)]) { + self.init(uniqueKeysWithValues: elements) + } +} + +extension LockedValue: ExpressibleByDictionaryLiteral where Value: ExpressibleByDictionaryLiteral & DictionaryInitialzable, Value.Key: Hashable { + @inlinable + public convenience init(dictionaryLiteral elements: (Value.Key, Value.Value)...) { + let elements = elements.map { ($0, $1) } + self.init(wrappedValue: Value(elements: elements)) + } +} + +extension AnyKeyPath: @unchecked Sendable {} diff --git a/Tests/JWSETKitTests/Cryptography/RFC7520SignatureTests.swift b/Tests/JWSETKitTests/Cryptography/RFC7520SignatureTests.swift index b8d0f84..b895cde 100644 --- a/Tests/JWSETKitTests/Cryptography/RFC7520SignatureTests.swift +++ b/Tests/JWSETKitTests/Cryptography/RFC7520SignatureTests.swift @@ -1,5 +1,5 @@ // -// RFC7520Tests.swift +// RFC7520SignatureTests.swift // // // Created by Amir Abbas Mousavian on 1/5/24. @@ -158,14 +158,13 @@ final class RFC7520Tests: XCTestCase { func testSignatureUnprotectedHeader() throws { let jws = try JWS(signatures: [ .init(protected: "eyJhbGciOiJIUzI1NiJ9".decoded, - unprotected: .init({ $0.keyId = "018c0ae5-4d9b-471b-bfd6-eef314bc7037" }), - signature: "bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20".decoded) + unprotected: .init { $0.keyId = "018c0ae5-4d9b-471b-bfd6-eef314bc7037" }, + signature: "bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20".decoded), ], payload: .init(encoded: payload.decoded)) XCTAssertNoThrow(try jws.verifySignature(using: RFC7520ExampleKeys.macSymmetricKey.validatingKey)) let encoder = JSONEncoder() - encoder.userInfo[.jwsEncodedRepresentation] = JSONWebSignatureRepresentation.jsonFlattened let jwsFlattened = try encoder.encode(jws) let jwsFlattenedValue = try JSONDecoder().decode(JSONWebValueStorage.self, from: jwsFlattened) XCTAssertTrue(jwsFlattenedValue.contains(key: "header")) @@ -181,17 +180,16 @@ final class RFC7520Tests: XCTestCase { func testSignatureProtectedContentOnly() throws { let jws = try JWS(signatures: [ .init(protected: JOSEHeader(), - unprotected: .init({ + unprotected: .init { $0.algorithm = JSONWebSignatureAlgorithm.hmacSHA256 $0.keyId = "018c0ae5-4d9b-471b-bfd6-eef314bc7037" - }), - signature: "xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk".decoded) + }, + signature: "xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk".decoded), ], payload: .init(encoded: payload.decoded)) XCTAssertNoThrow(try jws.verifySignature(using: RFC7520ExampleKeys.macSymmetricKey.validatingKey, strict: false)) let encoder = JSONEncoder() - encoder.userInfo[.jwsEncodedRepresentation] = JSONWebSignatureRepresentation.jsonFlattened let jwsFlattened = try encoder.encode(jws) let jwsFlattenedValue = try JSONDecoder().decode(JSONWebValueStorage.self, from: jwsFlattened) XCTAssertTrue(jwsFlattenedValue.contains(key: "header"))