11//Copyright (c) 2018 Michael Eisel. All rights reserved.
22
3+ import os
34import Foundation
45import ZippyJSONCFamily
56import JJLISO8601DateFormatter
@@ -37,19 +38,15 @@ func isOnSimulator() -> Bool {
3738 #endif
3839}
3940
40- public final class ZippyJSONDecoder {
41+ public final class ZippyJSONDecoder : Sendable {
4142 @available ( * , deprecated, message: " This flag is deprecated because full-precision parsing speed is now on par with imprecise, so it will just always use full-precision " )
42- public var zjd_fullPrecisionFloatParsing = true
43+ public let zjd_fullPrecisionFloatParsing = true
44+
45+ @Synchronized
4346 private static var _zjd_suppressWarnings : Bool = false
4447 public static var zjd_suppressWarnings : Bool {
45- get {
46- return _zjd_suppressWarnings
47- }
48- set {
49- objc_sync_enter ( self )
50- defer { objc_sync_exit ( self ) }
51- _zjd_suppressWarnings = newValue
52- }
48+ get { _zjd_suppressWarnings }
49+ set { _zjd_suppressWarnings = newValue }
5350 }
5451
5552 private func createContext( string: UnsafePointer < Int8 > , length: Int ) -> ContextPointer {
@@ -167,42 +164,66 @@ public final class ZippyJSONDecoder {
167164 }
168165 }
169166
167+ @Synchronized
168+ private var _userInfo : [ CodingUserInfoKey : Any ] = [ : ]
169+ public var userInfo : [ CodingUserInfoKey : Any ] {
170+ get { _userInfo }
171+ set { _userInfo = newValue }
172+ }
170173
171- public var userInfo : [ CodingUserInfoKey : Any ] = [ : ]
172-
173- public var nonConformingFloatDecodingStrategy : NonConformingFloatDecodingStrategy
174+ @Synchronized
175+ private var _nonConformingFloatDecodingStrategy : NonConformingFloatDecodingStrategy
176+ public var nonConformingFloatDecodingStrategy : NonConformingFloatDecodingStrategy {
177+ get { _nonConformingFloatDecodingStrategy }
178+ set { _nonConformingFloatDecodingStrategy = newValue }
179+ }
174180
175- public enum NonConformingFloatDecodingStrategy {
181+ public enum NonConformingFloatDecodingStrategy : Sendable {
176182 case `throw`
177183 case convertFromString( positiveInfinity: String , negativeInfinity: String , nan: String )
178184 }
179185
180- public var dataDecodingStrategy : DataDecodingStrategy
186+ @Synchronized
187+ private var _dataDecodingStrategy : DataDecodingStrategy
188+ public var dataDecodingStrategy : DataDecodingStrategy {
189+ get { _dataDecodingStrategy }
190+ set { _dataDecodingStrategy = newValue }
191+ }
181192
182- public enum DataDecodingStrategy {
193+ public enum DataDecodingStrategy : Sendable {
183194 case deferredToData
184195 case base64
185- case custom( ( Decoder ) throws -> Data )
196+ case custom( @ Sendable ( Decoder) throws -> Data )
186197 }
187198
188- public enum KeyDecodingStrategy {
199+ public enum KeyDecodingStrategy : Sendable {
189200 case useDefaultKeys
190201 case convertFromSnakeCase
191- case custom( ( [ CodingKey ] ) -> CodingKey )
202+ case custom( @ Sendable ( [ CodingKey ] ) -> CodingKey )
192203 }
193204
194- public var keyDecodingStrategy : KeyDecodingStrategy
205+ @Synchronized
206+ private var _keyDecodingStrategy : KeyDecodingStrategy
207+ public var keyDecodingStrategy : KeyDecodingStrategy {
208+ get { _keyDecodingStrategy }
209+ set { _keyDecodingStrategy = newValue }
210+ }
195211
196- public enum DateDecodingStrategy {
212+ public enum DateDecodingStrategy : Sendable {
197213 case deferredToDate
198214 case secondsSince1970
199215 case millisecondsSince1970
200216 case iso8601
201217 case formatted( DateFormatter )
202- case custom( ( Decoder ) throws -> Date )
218+ case custom( @ Sendable ( Decoder) throws -> Date )
203219 }
204220
205- public var dateDecodingStrategy : DateDecodingStrategy
221+ @Synchronized
222+ private var _dateDecodingStrategy : DateDecodingStrategy
223+ public var dateDecodingStrategy : DateDecodingStrategy {
224+ get { _dateDecodingStrategy }
225+ set { _dateDecodingStrategy = newValue }
226+ }
206227
207228 var convertCase : Bool {
208229 get {
@@ -216,10 +237,10 @@ public final class ZippyJSONDecoder {
216237 }
217238
218239 public init ( ) {
219- keyDecodingStrategy = . useDefaultKeys
220- dataDecodingStrategy = . base64
221- dateDecodingStrategy = . deferredToDate
222- nonConformingFloatDecodingStrategy = . throw
240+ _keyDecodingStrategy = . useDefaultKeys
241+ _dataDecodingStrategy = . base64
242+ _dateDecodingStrategy = . deferredToDate
243+ _nonConformingFloatDecodingStrategy = . throw
223244 }
224245}
225246
@@ -1057,3 +1078,63 @@ extension __JSONDecoder : SingleValueDecodingContainer {
10571078
10581079 // End
10591080}
1081+
1082+ @propertyWrapper
1083+ private final class Synchronized < Value> : @unchecked Sendable {
1084+ private let lock : LockProtocol
1085+ private var _wrappedValue : Value
1086+
1087+ init ( wrappedValue: Value ) {
1088+ self . lock = Lock ( )
1089+ _wrappedValue = wrappedValue
1090+ }
1091+
1092+ var wrappedValue : Value {
1093+ get {
1094+ lock. withLock {
1095+ _wrappedValue
1096+ }
1097+ }
1098+ set {
1099+ lock. withLock {
1100+ _wrappedValue = newValue
1101+ }
1102+ }
1103+ }
1104+ }
1105+
1106+ private final class Lock : LockProtocol {
1107+ let innerLock : LockProtocol
1108+
1109+ init ( ) {
1110+ // Use the lighter-weight `OSAllocatedUnfairLock` if available
1111+ if #available( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * ) {
1112+ innerLock = OSAllocatedUnfairLock ( )
1113+ } else {
1114+ innerLock = NSLock ( )
1115+ }
1116+ }
1117+
1118+ func withLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
1119+ return try innerLock. withLock ( body)
1120+ }
1121+ }
1122+
1123+ private protocol LockProtocol {
1124+ func withLock< T> ( _ body: ( ) throws -> T ) rethrows -> T
1125+ }
1126+
1127+ extension NSLock : LockProtocol {
1128+ func withLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
1129+ lock ( )
1130+ defer { unlock ( ) }
1131+ return try body ( )
1132+ }
1133+ }
1134+
1135+ @available ( macOS 13 . 0 , * )
1136+ extension OSAllocatedUnfairLock : LockProtocol {
1137+ func withLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
1138+ try withLockUnchecked { _ in try body ( ) }
1139+ }
1140+ }
0 commit comments