Skip to content

Commit 4de5273

Browse files
authored
Merge pull request #47 from orlandos-nl/jo/span-support
Support for Swift 6.2 spans
2 parents 7c8ce5b + 3332102 commit 4de5273

18 files changed

+1075
-300
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ Package.resolved
66
/Packages
77
/*.xcodeproj
88
Package.resolved
9+
Benchmarks/JSONBenchmark/.build
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
import Benchmark
2+
import Foundation
3+
import IkigaJSON
4+
5+
#if canImport(FoundationEssentials)
6+
import FoundationEssentials
7+
#endif
8+
9+
// MARK: - Test Data Structures
10+
11+
struct SmallUser: Codable {
12+
let id: Int
13+
let name: String
14+
let email: String
15+
let active: Bool
16+
}
17+
18+
struct MediumUser: Codable {
19+
let id: Int
20+
let username: String
21+
let email: String
22+
let firstName: String
23+
let lastName: String
24+
let age: Int
25+
let isActive: Bool
26+
let createdAt: String
27+
let roles: [String]
28+
let settings: Settings
29+
30+
struct Settings: Codable {
31+
let theme: String
32+
let notifications: Bool
33+
let language: String
34+
}
35+
}
36+
37+
struct LargePayload: Codable {
38+
let users: [MediumUser]
39+
let metadata: Metadata
40+
let tags: [String]
41+
42+
struct Metadata: Codable {
43+
let total: Int
44+
let page: Int
45+
let perPage: Int
46+
let totalPages: Int
47+
}
48+
}
49+
50+
// MARK: - Sample JSON Data
51+
52+
let smallJSON = """
53+
{
54+
"id": 12345,
55+
"name": "John Doe",
56+
"email": "john.doe@example.com",
57+
"active": true
58+
}
59+
""".data(using: .utf8)!
60+
61+
let mediumJSON = """
62+
{
63+
"id": 12345,
64+
"username": "johndoe",
65+
"email": "john.doe@example.com",
66+
"firstName": "John",
67+
"lastName": "Doe",
68+
"age": 32,
69+
"isActive": true,
70+
"createdAt": "2024-01-15T10:30:00Z",
71+
"roles": ["admin", "user", "moderator"],
72+
"settings": {
73+
"theme": "dark",
74+
"notifications": true,
75+
"language": "en-US"
76+
}
77+
}
78+
""".data(using: .utf8)!
79+
80+
let largeJSON: Data = {
81+
let users = (0..<100).map { i in
82+
"""
83+
{
84+
"id": \(i),
85+
"username": "user\(i)",
86+
"email": "user\(i)@example.com",
87+
"firstName": "First\(i)",
88+
"lastName": "Last\(i)",
89+
"age": \(20 + (i % 50)),
90+
"isActive": \(i % 2 == 0),
91+
"createdAt": "2024-01-15T10:30:00Z",
92+
"roles": ["user", "member"],
93+
"settings": {
94+
"theme": "light",
95+
"notifications": false,
96+
"language": "en-US"
97+
}
98+
}
99+
"""
100+
}.joined(separator: ",\n")
101+
102+
return """
103+
{
104+
"users": [\(users)],
105+
"metadata": {
106+
"total": 100,
107+
"page": 1,
108+
"perPage": 100,
109+
"totalPages": 1
110+
},
111+
"tags": ["api", "users", "export", "batch", "data"]
112+
}
113+
""".data(using: .utf8)!
114+
}()
115+
116+
// Pre-create encoded data for encoding benchmarks
117+
let smallUser = SmallUser(id: 12345, name: "John Doe", email: "john.doe@example.com", active: true)
118+
let mediumUser = MediumUser(
119+
id: 12345,
120+
username: "johndoe",
121+
email: "john.doe@example.com",
122+
firstName: "John",
123+
lastName: "Doe",
124+
age: 32,
125+
isActive: true,
126+
createdAt: "2024-01-15T10:30:00Z",
127+
roles: ["admin", "user", "moderator"],
128+
settings: .init(theme: "dark", notifications: true, language: "en-US")
129+
)
130+
131+
let largePayload: LargePayload = {
132+
let users = (0..<100).map { i in
133+
MediumUser(
134+
id: i,
135+
username: "user\(i)",
136+
email: "user\(i)@example.com",
137+
firstName: "First\(i)",
138+
lastName: "Last\(i)",
139+
age: 20 + (i % 50),
140+
isActive: i % 2 == 0,
141+
createdAt: "2024-01-15T10:30:00Z",
142+
roles: ["user", "member"],
143+
settings: .init(theme: "light", notifications: false, language: "en-US")
144+
)
145+
}
146+
return LargePayload(
147+
users: users,
148+
metadata: .init(total: 100, page: 1, perPage: 100, totalPages: 1),
149+
tags: ["api", "users", "export", "batch", "data"]
150+
)
151+
}()
152+
153+
// MARK: - Benchmarks
154+
155+
let benchmarks: @Sendable () -> Void = {
156+
// ============================================
157+
// DECODING BENCHMARKS
158+
// ============================================
159+
160+
Benchmark.defaultConfiguration = .init(
161+
metrics: [
162+
.cpuTotal,
163+
.wallClock,
164+
.throughput,
165+
.peakMemoryResident,
166+
.mallocCountTotal
167+
],
168+
warmupIterations: 10
169+
)
170+
171+
// --- Small Payload Decoding ---
172+
173+
Benchmark("Decode Small - Foundation") { benchmark in
174+
let decoder = JSONDecoder()
175+
for _ in benchmark.scaledIterations {
176+
blackHole(try! decoder.decode(SmallUser.self, from: smallJSON))
177+
}
178+
}
179+
180+
Benchmark("Decode Small - IkigaJSON") { benchmark in
181+
let decoder = IkigaJSONDecoder()
182+
for _ in benchmark.scaledIterations {
183+
blackHole(try! decoder.decode(SmallUser.self, from: smallJSON))
184+
}
185+
}
186+
187+
// --- Medium Payload Decoding ---
188+
189+
Benchmark("Decode Medium - Foundation") { benchmark in
190+
let decoder = JSONDecoder()
191+
for _ in benchmark.scaledIterations {
192+
blackHole(try! decoder.decode(MediumUser.self, from: mediumJSON))
193+
}
194+
}
195+
196+
Benchmark("Decode Medium - IkigaJSON") { benchmark in
197+
let decoder = IkigaJSONDecoder()
198+
for _ in benchmark.scaledIterations {
199+
blackHole(try! decoder.decode(MediumUser.self, from: mediumJSON))
200+
}
201+
}
202+
203+
// --- Large Payload Decoding ---
204+
205+
Benchmark("Decode Large - Foundation") { benchmark in
206+
let decoder = JSONDecoder()
207+
for _ in benchmark.scaledIterations {
208+
blackHole(try! decoder.decode(LargePayload.self, from: largeJSON))
209+
}
210+
}
211+
212+
Benchmark("Decode Large - IkigaJSON") { benchmark in
213+
let decoder = IkigaJSONDecoder()
214+
for _ in benchmark.scaledIterations {
215+
blackHole(try! decoder.decode(LargePayload.self, from: largeJSON))
216+
}
217+
}
218+
219+
// ============================================
220+
// ENCODING BENCHMARKS
221+
// ============================================
222+
223+
// --- Small Payload Encoding ---
224+
225+
Benchmark("Encode Small - Foundation") { benchmark in
226+
let encoder = JSONEncoder()
227+
for _ in benchmark.scaledIterations {
228+
blackHole(try! encoder.encode(smallUser))
229+
}
230+
}
231+
232+
Benchmark("Encode Small - IkigaJSON") { benchmark in
233+
let encoder = IkigaJSONEncoder()
234+
for _ in benchmark.scaledIterations {
235+
blackHole(try! encoder.encode(smallUser))
236+
}
237+
}
238+
239+
// --- Medium Payload Encoding ---
240+
241+
Benchmark("Encode Medium - Foundation") { benchmark in
242+
let encoder = JSONEncoder()
243+
for _ in benchmark.scaledIterations {
244+
blackHole(try! encoder.encode(mediumUser))
245+
}
246+
}
247+
248+
Benchmark("Encode Medium - IkigaJSON") { benchmark in
249+
let encoder = IkigaJSONEncoder()
250+
for _ in benchmark.scaledIterations {
251+
blackHole(try! encoder.encode(mediumUser))
252+
}
253+
}
254+
255+
// --- Large Payload Encoding ---
256+
257+
Benchmark("Encode Large - Foundation") { benchmark in
258+
let encoder = JSONEncoder()
259+
for _ in benchmark.scaledIterations {
260+
blackHole(try! encoder.encode(largePayload))
261+
}
262+
}
263+
264+
Benchmark("Encode Large - IkigaJSON") { benchmark in
265+
let encoder = IkigaJSONEncoder()
266+
for _ in benchmark.scaledIterations {
267+
blackHole(try! encoder.encode(largePayload))
268+
}
269+
}
270+
271+
// ============================================
272+
// ROUND-TRIP BENCHMARKS
273+
// ============================================
274+
275+
Benchmark("Round-trip Medium - Foundation") { benchmark in
276+
let encoder = JSONEncoder()
277+
let decoder = JSONDecoder()
278+
for _ in benchmark.scaledIterations {
279+
let data = try! encoder.encode(mediumUser)
280+
blackHole(try! decoder.decode(MediumUser.self, from: data))
281+
}
282+
}
283+
284+
Benchmark("Round-trip Medium - IkigaJSON") { benchmark in
285+
let encoder = IkigaJSONEncoder()
286+
let decoder = IkigaJSONDecoder()
287+
for _ in benchmark.scaledIterations {
288+
let data = try! encoder.encode(mediumUser)
289+
blackHole(try! decoder.decode(MediumUser.self, from: data))
290+
}
291+
}
292+
}
293+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:6.0
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "JSONBenchmark",
6+
platforms: [
7+
.macOS(.v14)
8+
],
9+
dependencies: [
10+
.package(path: "../.."),
11+
.package(url: "https://github.com/ordo-one/package-benchmark", from: "1.27.0"),
12+
],
13+
targets: [
14+
.executableTarget(
15+
name: "JSONBenchmark",
16+
dependencies: [
17+
.product(name: "IkigaJSON", package: "IkigaJSON"),
18+
.product(name: "Benchmark", package: "package-benchmark"),
19+
],
20+
path: ".",
21+
exclude: ["Package.swift"],
22+
plugins: [
23+
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
24+
]
25+
),
26+
]
27+
)
28+

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ else()
1010
include(ExternalProject)
1111
endif()
1212

13-
file(GLOB_RECURSE JSON_CORE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/Sources/JSONCore/*.swift")
13+
file(GLOB_RECURSE JSON_CORE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/Sources/_JSONCore/*.swift")
1414
add_library(JSON ${JSON_CORE_SOURCES})
1515

1616
if(DEFINED ENV{IDF_PATH})

Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ let package = Package(
2323
targets: [
2424
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
2525
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
26-
.target(name: "_JSONCore"),
26+
.target(
27+
name: "_JSONCore"
28+
),
2729
.target(
2830
name: "_NIOJSON",
2931
dependencies: [

0 commit comments

Comments
 (0)