taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 0ce3022da6b1c7cb505590f91421049347aa4280
parent df19d0370fba2735062d565a45c3461ea77c7094
Author: Marc Stibane <marc@taler.net>
Date:   Thu, 29 Feb 2024 13:54:37 +0100

Helpers

Diffstat:
MTalerWallet.xcodeproj/project.pbxproj | 12++++++++++++
ATalerWallet1/Helper/Atomic.swift | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATalerWallet1/Helper/CStringArray.swift | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 + } + } +}