Skip to content

Commit cad55c1

Browse files
KM-13582 Remove PIALibraryUtilObjC by converting legacy Objective-C code into Swift inside regular PIALibrary
1 parent 0fd6b4e commit cad55c1

25 files changed

Lines changed: 510 additions & 587 deletions

File tree

LocalPackages/PIALibrary/Package.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ let package = Package(
3434
name: "PIALibrary",
3535
dependencies: [
3636
"Gloss",
37-
"PIALibraryUtilObjC",
3837
.product(name: "Logging", package: "swift-log"),
3938
.product(name: "PopupDialog", package: "PopupDialog", condition: .when(platforms: [.iOS])),
4039
.product(name: "SwiftEntryKit", package: "SwiftEntryKit", condition: .when(platforms: [.iOS])),
@@ -53,10 +52,6 @@ let package = Package(
5352
.process("Resources")
5453
]
5554
),
56-
.target(
57-
name: "PIALibraryUtilObjC",
58-
dependencies: []
59-
),
6055
.testTarget(
6156
name: "PIALibraryTests",
6257
dependencies: [

LocalPackages/PIALibrary/Sources/PIALibrary/Daemons/PingTask.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
//
2222

2323
import Foundation
24-
import __PIALibraryNative
2524

2625
private let log = PIALogger.logger(for: PingTask.self)
2726

LocalPackages/PIALibrary/Sources/PIALibrary/Daemons/ServersPinger.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
//
2222

2323
import Foundation
24-
import __PIALibraryNative
2524

2625
private let log = PIALogger.logger(for: ServersPinger.self)
2726

LocalPackages/PIALibrary/Sources/PIALibrary/Server/DefaultServerProvider.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
//
2222

2323
import Foundation
24-
import __PIALibraryNative
2524

2625
fileprivate let log = PIALogger.logger(for: DefaultServerProvider.self)
2726

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//
2+
// Data+Compression.swift
3+
// PIALibrary
4+
//
5+
// Created by Diego Trevisan on 29/12/25.
6+
// Copyright © 2025 Private Internet Access, Inc.
7+
//
8+
// This file is part of the Private Internet Access iOS Client.
9+
//
10+
// The Private Internet Access iOS Client is free software: you can redistribute it and/or
11+
// modify it under the terms of the GNU General Public License as published by the Free
12+
// Software Foundation, either version 3 of the License, or (at your option) any later version.
13+
//
14+
// The Private Internet Access iOS Client is distributed in the hope that it will be useful,
15+
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17+
// details.
18+
//
19+
// You should have received a copy of the GNU General Public License along with the Private
20+
// Internet Access iOS Client. If not, see <https://www.gnu.org/licenses/>.
21+
//
22+
23+
import Foundation
24+
import zlib
25+
26+
private let compressionBlockSize = 16384
27+
28+
extension Data {
29+
/// Compresses the data using zlib deflate algorithm.
30+
/// - Returns: The compressed data, or nil if compression fails.
31+
func deflated() -> Data? {
32+
guard !isEmpty else {
33+
return self
34+
}
35+
36+
var stream = z_stream()
37+
stream.zalloc = nil
38+
stream.zfree = nil
39+
stream.opaque = nil
40+
stream.total_out = 0
41+
42+
guard deflateInit2_(&stream, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY, ZLIB_VERSION, Int32(MemoryLayout<z_stream>.size)) == Z_OK else {
43+
return nil
44+
}
45+
46+
defer {
47+
deflateEnd(&stream)
48+
}
49+
50+
var compressed = Data(count: compressionBlockSize)
51+
var compressedSize = compressed.count
52+
53+
let result: Data? = withUnsafeBytes { (inputBytes: UnsafeRawBufferPointer) -> Data? in
54+
guard let inputBaseAddress = inputBytes.bindMemory(to: UInt8.self).baseAddress else {
55+
return nil
56+
}
57+
stream.next_in = UnsafeMutablePointer<UInt8>(mutating: inputBaseAddress)
58+
stream.avail_in = uInt(count)
59+
60+
repeat {
61+
if Int(stream.total_out) >= compressedSize {
62+
compressedSize += compressionBlockSize
63+
compressed.count = compressedSize
64+
}
65+
66+
let status = compressed.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) -> Int32 in
67+
guard let outputBaseAddress = outputBytes.bindMemory(to: UInt8.self).baseAddress else {
68+
return Z_STREAM_ERROR
69+
}
70+
stream.next_out = outputBaseAddress.advanced(by: Int(stream.total_out))
71+
stream.avail_out = uInt(compressedSize - Int(stream.total_out))
72+
return deflate(&stream, Z_FINISH)
73+
}
74+
75+
if status < 0 {
76+
return nil
77+
}
78+
} while stream.avail_out == 0
79+
80+
compressed.count = Int(stream.total_out)
81+
return compressed
82+
}
83+
84+
return result
85+
}
86+
87+
/// Decompresses the data using zlib inflate algorithm.
88+
/// - Returns: The decompressed data, or nil if decompression fails.
89+
func inflated() -> Data? {
90+
guard !isEmpty else {
91+
return self
92+
}
93+
94+
let fullLength = count
95+
let halfLength = count / 2
96+
97+
var stream = z_stream()
98+
stream.zalloc = nil
99+
stream.zfree = nil
100+
stream.total_out = 0
101+
102+
guard inflateInit2_(&stream, MAX_WBITS, ZLIB_VERSION, Int32(MemoryLayout<z_stream>.size)) == Z_OK else {
103+
return nil
104+
}
105+
106+
defer {
107+
inflateEnd(&stream)
108+
}
109+
110+
var decompressed = Data(count: fullLength + halfLength)
111+
var decompressedSize = decompressed.count
112+
var done = false
113+
114+
let result: Data? = withUnsafeBytes { (inputBytes: UnsafeRawBufferPointer) -> Data? in
115+
guard let inputBaseAddress = inputBytes.bindMemory(to: UInt8.self).baseAddress else {
116+
return nil
117+
}
118+
stream.next_in = UnsafeMutablePointer<UInt8>(mutating: inputBaseAddress)
119+
stream.avail_in = uInt(count)
120+
121+
while !done {
122+
if Int(stream.total_out) >= decompressedSize {
123+
decompressedSize += halfLength
124+
decompressed.count = decompressedSize
125+
}
126+
127+
let status = decompressed.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) -> Int32 in
128+
guard let outputBaseAddress = outputBytes.bindMemory(to: UInt8.self).baseAddress else {
129+
return Z_STREAM_ERROR
130+
}
131+
stream.next_out = outputBaseAddress.advanced(by: Int(stream.total_out))
132+
stream.avail_out = uInt(decompressedSize - Int(stream.total_out))
133+
return inflate(&stream, Z_SYNC_FLUSH)
134+
}
135+
136+
if status == Z_STREAM_END {
137+
done = true
138+
} else if status != Z_OK {
139+
return nil
140+
}
141+
}
142+
143+
guard inflateEnd(&stream) == Z_OK else {
144+
return nil
145+
}
146+
147+
guard done else {
148+
return nil
149+
}
150+
151+
decompressed.count = Int(stream.total_out)
152+
return decompressed
153+
}
154+
155+
return result
156+
}
157+
}

LocalPackages/PIALibrary/Sources/PIALibrary/Util/Macros+Pinger.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
//
2222

2323
import Foundation
24-
import __PIALibraryNative
2524

2625
/// The IP protocol over which to issue the ping.
2726
public enum PingerProtocol {
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//
2+
// Pinger.swift
3+
// PIALibrary
4+
//
5+
// Created by Diego Trevisan on 29/12/25.
6+
// Copyright © 2025 Private Internet Access, Inc.
7+
//
8+
// This file is part of the Private Internet Access iOS Client.
9+
//
10+
// The Private Internet Access iOS Client is free software: you can redistribute it and/or
11+
// modify it under the terms of the GNU General Public License as published by the Free
12+
// Software Foundation, either version 3 of the License, or (at your option) any later version.
13+
//
14+
// The Private Internet Access iOS Client is distributed in the hope that it will be useful,
15+
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17+
// details.
18+
//
19+
// You should have received a copy of the GNU General Public License along with the Private
20+
// Internet Access iOS Client. If not, see <https://www.gnu.org/licenses/>.
21+
//
22+
23+
import Foundation
24+
25+
protocol Pinger {
26+
func sendPing() -> Int?
27+
func setTimeout(_ timeout: Int)
28+
}
29+
30+
class TCPPinger: Pinger {
31+
private let hostname: String
32+
private let port: UInt16
33+
private var timeout: Int = 0
34+
35+
init(hostname: String, port: UInt16) {
36+
self.hostname = hostname
37+
self.port = port
38+
}
39+
40+
func setTimeout(_ timeout: Int) {
41+
self.timeout = timeout
42+
}
43+
44+
func sendPing() -> Int? {
45+
let descriptor = socket(PF_INET, SOCK_STREAM, 0)
46+
guard descriptor != -1 else {
47+
return nil
48+
}
49+
50+
defer {
51+
close(descriptor)
52+
}
53+
54+
var address = sockaddr_in()
55+
address.sin_port = port.bigEndian
56+
address.sin_addr.s_addr = inet_addr(hostname)
57+
address.sin_family = sa_family_t(AF_INET)
58+
address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
59+
60+
let now = Date.timeIntervalSinceReferenceDate
61+
62+
let result = withUnsafePointer(to: &address) { pointer in
63+
pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPointer in
64+
connect(descriptor, sockaddrPointer, socklen_t(MemoryLayout<sockaddr_in>.size))
65+
}
66+
}
67+
68+
let responseTime = Int((Date.timeIntervalSinceReferenceDate - now) * 1000.0)
69+
70+
guard result == 0 else {
71+
return nil
72+
}
73+
74+
return responseTime
75+
}
76+
}
77+
78+
class UDPPinger: Pinger {
79+
private let hostname: String
80+
private let port: UInt16
81+
private var timeout: Int = 0
82+
83+
init(hostname: String, port: UInt16) {
84+
self.hostname = hostname
85+
self.port = port
86+
}
87+
88+
func setTimeout(_ timeout: Int) {
89+
self.timeout = timeout
90+
}
91+
92+
func sendPing() -> Int? {
93+
let descriptor = socket(PF_INET, SOCK_DGRAM, 0)
94+
guard descriptor != -1 else {
95+
return nil
96+
}
97+
98+
defer {
99+
close(descriptor)
100+
}
101+
102+
var address = sockaddr_in()
103+
address.sin_port = port.bigEndian
104+
address.sin_addr.s_addr = inet_addr(hostname)
105+
address.sin_family = sa_family_t(AF_INET)
106+
address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
107+
108+
// Set timeout if specified
109+
if timeout > 0 {
110+
var tv = timeval()
111+
let usecs = Int(timeout) * 1000
112+
tv.tv_sec = usecs / 1_000_000
113+
tv.tv_usec = Int32(usecs % 1_000_000)
114+
115+
setsockopt(descriptor, SOL_SOCKET, SO_SNDTIMEO, &tv, socklen_t(MemoryLayout<timeval>.size))
116+
setsockopt(descriptor, SOL_SOCKET, SO_RCVTIMEO, &tv, socklen_t(MemoryLayout<timeval>.size))
117+
}
118+
119+
let now = Date.timeIntervalSinceReferenceDate
120+
121+
// Send dummy byte
122+
let dummyByte: [UInt8] = [UInt8(ascii: "a")]
123+
let sendResult = withUnsafePointer(to: &address) { pointer in
124+
pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPointer in
125+
sendto(descriptor, dummyByte, dummyByte.count, 0, sockaddrPointer, socklen_t(MemoryLayout<sockaddr_in>.size))
126+
}
127+
}
128+
129+
guard sendResult > 0 else {
130+
return nil
131+
}
132+
133+
// Receive response
134+
var fromAddress = sockaddr()
135+
var fromAddressSize = socklen_t(MemoryLayout<sockaddr>.size)
136+
var received = [UInt8](repeating: 0, count: 1)
137+
138+
let recvResult = recvfrom(descriptor, &received, 1, 0, &fromAddress, &fromAddressSize)
139+
140+
let responseTime = Int((Date.timeIntervalSinceReferenceDate - now) * 1000)
141+
142+
guard recvResult != -1 else {
143+
return nil
144+
}
145+
146+
return responseTime
147+
}
148+
}

0 commit comments

Comments
 (0)