commit 0ce3022da6b1c7cb505590f91421049347aa4280
parent df19d0370fba2735062d565a45c3461ea77c7094
Author: Marc Stibane <marc@taler.net>
Date: Thu, 29 Feb 2024 13:54:37 +0100
Helpers
Diffstat:
3 files changed, 148 insertions(+), 0 deletions(-)
diff --git a/TalerWallet.xcodeproj/project.pbxproj b/TalerWallet.xcodeproj/project.pbxproj
@@ -240,6 +240,10 @@
4ECB62802A0BA6DF004ABBB7 /* Model+P2P.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */; };
4ECB62822A0BB01D004ABBB7 /* SelectDays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */; };
4ED2F94B2A278F5100453B40 /* ThreeAmountsV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift */; };
+ 4ED80E882B8F5FB8008BD576 /* CStringArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED80E872B8F5FB8008BD576 /* CStringArray.swift */; };
+ 4ED80E892B8F5FB8008BD576 /* CStringArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED80E872B8F5FB8008BD576 /* CStringArray.swift */; };
+ 4ED80E8B2B8F60E7008BD576 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED80E8A2B8F60E7008BD576 /* Atomic.swift */; };
+ 4ED80E8C2B8F60E7008BD576 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED80E8A2B8F60E7008BD576 /* Atomic.swift */; };
4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; };
4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; };
4EE171882B49635800BF9FF5 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE171872B49635800BF9FF5 /* MarkdownUI */; };
@@ -425,6 +429,8 @@
4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Model+P2P.swift"; sourceTree = "<group>"; };
4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectDays.swift; sourceTree = "<group>"; };
4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreeAmountsV.swift; sourceTree = "<group>"; };
+ 4ED80E872B8F5FB8008BD576 /* CStringArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CStringArray.swift; sourceTree = "<group>"; };
+ 4ED80E8A2B8F60E7008BD576 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
4EDBDCD82AB787CB00925C02 /* CallStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallStack.swift; sourceTree = "<group>"; };
4EEC118C2B83DE4700146CFF /* AmountInputV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmountInputV.swift; sourceTree = "<group>"; };
4EEC11922B83FB7A00146CFF /* SubjectInputV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectInputV.swift; sourceTree = "<group>"; };
@@ -600,7 +606,9 @@
children = (
4E363CBD2A23CB2100D7E98C /* AnyTransition+backslide.swift */,
4E3327B92AD1635100BF5AD6 /* AsyncSemaphore.swift */,
+ 4ED80E8A2B8F60E7008BD576 /* Atomic.swift */,
4EDBDCD82AB787CB00925C02 /* CallStack.swift */,
+ 4ED80E872B8F5FB8008BD576 /* CStringArray.swift */,
4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */,
4EAD117529F672FA008EDD0B /* KeyboardResponder.swift */,
4E363CC12A2621C200D7E98C /* LocalizedAlertError.swift */,
@@ -1162,6 +1170,7 @@
4E3EAE692A990778009F1BE8 /* URLSheet.swift in Sources */,
4E3EAE6A2A990778009F1BE8 /* ThreeAmountsV.swift in Sources */,
4E3EAE6B2A990778009F1BE8 /* Model+Withdraw.swift in Sources */,
+ 4ED80E882B8F5FB8008BD576 /* CStringArray.swift in Sources */,
4E3EAE6C2A990778009F1BE8 /* ExchangeSectionView.swift in Sources */,
4E3EAE6D2A990778009F1BE8 /* P2PSubjectV.swift in Sources */,
4E6EF56B2B65A33300AF252A /* PaymentDone.swift in Sources */,
@@ -1169,6 +1178,7 @@
4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift in Sources */,
4E3EAE702A990778009F1BE8 /* CurrencyInputView.swift in Sources */,
4E3EAE712A990778009F1BE8 /* URL+id+iban.swift in Sources */,
+ 4ED80E8B2B8F60E7008BD576 /* Atomic.swift in Sources */,
4EEC3A712B2285A200D05F9D /* WithdrawExchangeV.swift in Sources */,
4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */,
4E3EAE722A990778009F1BE8 /* RequestPayment.swift in Sources */,
@@ -1275,6 +1285,7 @@
4EB0955A2989CBFE0043A8A1 /* URLSheet.swift in Sources */,
4ED2F94B2A278F5100453B40 /* ThreeAmountsV.swift in Sources */,
4EB095622989CBFE0043A8A1 /* Model+Withdraw.swift in Sources */,
+ 4ED80E892B8F5FB8008BD576 /* CStringArray.swift in Sources */,
4EC90C782A1B528B0071DC58 /* ExchangeSectionView.swift in Sources */,
4E7940DE29FC307C00A9AEA1 /* P2PSubjectV.swift in Sources */,
4E6EF56C2B65A33300AF252A /* PaymentDone.swift in Sources */,
@@ -1282,6 +1293,7 @@
4EB0950A2989CB7C0043A8A1 /* TalerStrings.swift in Sources */,
4EA551252A2C923600FEC9A8 /* CurrencyInputView.swift in Sources */,
4E363CBC2A237E0900D7E98C /* URL+id+iban.swift in Sources */,
+ 4ED80E8C2B8F60E7008BD576 /* Atomic.swift in Sources */,
4EEC3A722B2285A200D05F9D /* WithdrawExchangeV.swift in Sources */,
4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */,
4E9320452A1645B600A87B0E /* RequestPayment.swift in Sources */,
diff --git a/TalerWallet1/Helper/Atomic.swift b/TalerWallet1/Helper/Atomic.swift
@@ -0,0 +1,62 @@
+//
+// Copyright 2023 Marc Stibane
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+// and associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+import Foundation
+
+@propertyWrapper
+class Atomic<Value> where Value: BinaryInteger {
+
+ private let lock: NSLock
+ private var value: Value
+
+ init(default: Value) {
+ self.lock = NSLock()
+ self.value = `default`
+ }
+
+ var wrappedValue: Value {
+ get {
+ lock.lock()
+ defer { lock.unlock() }
+ return value
+ }
+ set {
+ lock.lock()
+ value = newValue
+ lock.unlock()
+ }
+ }
+
+ var projectedValue: Atomic<Value> { self }
+
+ @discardableResult
+ func atomicIncrement() -> Value {
+ lock.lock()
+ defer { lock.unlock() }
+ self.value += 1
+ return value
+ }
+
+ @discardableResult
+ func atomicAdd(_ value: Value) -> Value {
+ lock.lock()
+ defer { lock.unlock() }
+ self.value += value
+ return value
+ }
+}
diff --git a/TalerWallet1/Helper/CStringArray.swift b/TalerWallet1/Helper/CStringArray.swift
@@ -0,0 +1,74 @@
+//
+// Copyright 2020 Robert Salesas
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+// and associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+import Foundation
+
+/// `CStringArray` represents a C null-terminated array of pointers to C strings.
+///
+/// The lifetime of the C strings will correspond to the lifetime of the `CStringArray`
+/// instance so be careful about copying the buffer as it may contain dangling pointers.
+
+public struct CStringArray {
+ public let pointer: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
+ public let count: Int
+ private var data: Data
+
+ public init(_ array: [String]) {
+ let count = array.count
+
+ // Allocate memory to hold the CStrings and a terminating nil
+ let pointer = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>.allocate(capacity: count + 1)
+ pointer.initialize(repeating: nil, count: count + 1) // Implicit terminating nil at the end of the array
+
+ // Populate the allocated memory with pointers to CStrings
+ var e = 0
+ array.forEach {
+ pointer[e] = strdup($0)
+ e += 1
+ }
+
+ // This uses the deallocator available on the data structure as a solution to the fact that structs do not have `deinit`
+ self.data = Data(bytesNoCopy: pointer, count: MemoryLayout<UnsafeMutablePointer<CChar>>.size * count, deallocator: .custom({_,_ in
+ for i in 0...count - 1 {
+ free(pointer[i])
+ }
+ pointer.deallocate()
+ }))
+
+ self.pointer = pointer
+ self.count = array.count
+ }
+
+ public subscript(index: Data.Index) -> UnsafeMutablePointer<CChar>? {
+ get {
+ precondition(index >= 0 && index < count, "Index out of range")
+ return pointer[index]
+ }
+ }
+
+ public subscript(index: Data.Index) -> String? {
+ get {
+ precondition(index >= 0 && index < count, "Index out of range")
+ if let pointee = pointer[index] {
+ return String(cString: pointee)
+ }
+
+ return nil
+ }
+ }
+}