// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. import Foundation // MARK: - String public extension String { var cArray: [CChar] { [UInt8](self.utf8).map { CChar(bitPattern: $0) } } /// Initialize with an optional pointer and a specific length init?(pointer: UnsafeRawPointer?, length: Int, encoding: String.Encoding = .utf8) { guard let pointer: UnsafeRawPointer = pointer, let result: String = String(data: Data(bytes: pointer, count: length), encoding: encoding) else { return nil } self = result } init( libSessionVal: T, fixedLength: Int? = .none ) { guard let fixedLength: Int = fixedLength else { // Note: The `String(cString:)` function requires that the value is null-terminated // so add a null-termination character if needed self = String( cString: withUnsafeBytes(of: libSessionVal) { [UInt8]($0) } .nullTerminated() ) return } guard let fixedLengthData: Data = Data( libSessionVal: libSessionVal, count: fixedLength, nullIfEmpty: true ), let result: String = String(data: fixedLengthData, encoding: .utf8) else { self = "" return } self = result } init?( libSessionVal: T, fixedLength: Int? = .none, nullIfEmpty: Bool ) { let result = String(libSessionVal: libSessionVal, fixedLength: fixedLength) guard !nullIfEmpty || !result.isEmpty else { return nil } self = result } func toLibSession() -> T { let targetSize: Int = MemoryLayout.stride var dataMatchingDestinationSize: [CChar] = [CChar](repeating: 0, count: targetSize) dataMatchingDestinationSize.replaceSubrange( 0.. { func toLibSession() -> T { switch self { case .some(let value): return value.toLibSession() case .none: return "".toLibSession() } } } // MARK: - Data public extension Data { var cArray: [UInt8] { [UInt8](self) } init(libSessionVal: T, count: Int) { let result: Data = Swift.withUnsafePointer(to: libSessionVal) { Data(bytes: $0, count: count) } self = result } init?(libSessionVal: T, count: Int, nullIfEmpty: Bool) { let result: Data = Data(libSessionVal: libSessionVal, count: count) // If all of the values are 0 then return the data as null guard !nullIfEmpty || result.contains(where: { $0 != 0 }) else { return nil } self = result } func toLibSession() -> T { let targetSize: Int = MemoryLayout.stride var dataMatchingDestinationSize: Data = Data(count: targetSize) dataMatchingDestinationSize.replaceSubrange( 0.. { func toLibSession() -> T { switch self { case .some(let value): return value.toLibSession() case .none: return Data().toLibSession() } } } // MARK: - Array public extension Array where Element == String { init?( pointer: UnsafeMutablePointer?>?, count: Int? ) { guard let pointee: UnsafeMutablePointer = pointer?.pointee, let count: Int = count else { return nil } self = (0..?>?, count: Int?, defaultValue: [String] ) { self = ([String](pointer: pointer, count: count) ?? defaultValue) } } public extension Array where Element == CChar { func nullTerminated() -> [Element] { guard self.last != CChar(0) else { return self } return self.appending(CChar(0)) } } public extension Array where Element == UInt8 { func nullTerminated() -> [Element] { guard self.last != UInt8(0) else { return self } return self.appending(UInt8(0)) } }