Skip to content

Commit 4ca7f5f

Browse files
committed
[Macros] Implement own JSON encoder/decoder
Inspired by swift-foundation's JSONEncoder/JSONDecoder. But basically a re-implementation of Encoder / Decoder. (cherry picked from commit 61c7338)
1 parent 305b5e5 commit 4ca7f5f

File tree

5 files changed

+1581
-2
lines changed

5 files changed

+1581
-2
lines changed

Sources/SwiftCompilerPlugin/CompilerPlugin.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,9 @@ internal struct PluginHostConnection: MessageConnection {
185185

186186
func sendMessage<TX: Encodable>(_ message: TX) throws {
187187
// Encode the message as JSON.
188-
let payload = try JSONEncoder().encode(message)
188+
let payload = try PluginMessageJSON.encode(message).withUnsafeBufferPointer { buffer in
189+
Data(buffer: buffer)
190+
}
189191

190192
// Write the header (a 64-bit length field in little endian byte order).
191193
var count = UInt64(payload.count).littleEndian
@@ -226,7 +228,9 @@ internal struct PluginHostConnection: MessageConnection {
226228
}
227229

228230
// Decode and return the message.
229-
return try JSONDecoder().decode(RX.self, from: payload)
231+
return try payload.withUnsafeBytes { buffer in
232+
return try PluginMessageJSON.decode(RX.self, from: buffer.bindMemory(to: UInt8.self))
233+
}
230234
}
231235

232236
enum PluginMessageError: Swift.Error {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
2+
//===----------------------------------------------------------------------===//
3+
// Coding Path Node
4+
//===----------------------------------------------------------------------===//
5+
6+
// This construction allows overall fewer and smaller allocations as the coding path is modified.
7+
internal enum _CodingPathNode {
8+
case root
9+
indirect case node(CodingKey, _CodingPathNode)
10+
indirect case indexNode(Int, _CodingPathNode)
11+
12+
var path : [any CodingKey] {
13+
switch self {
14+
case .root:
15+
return []
16+
case let .node(key, parent):
17+
return parent.path + [key]
18+
case let .indexNode(index, parent):
19+
return parent.path + [_CodingKey(index: index)]
20+
}
21+
}
22+
23+
@inline(__always)
24+
func appending(_ key: __owned (some CodingKey)?) -> _CodingPathNode {
25+
if let key {
26+
return .node(key, self)
27+
} else {
28+
return self
29+
}
30+
}
31+
32+
@inline(__always)
33+
func path(byAppending key: __owned (some CodingKey)?) -> [CodingKey] {
34+
if let key {
35+
return self.path + [key]
36+
}
37+
return self.path
38+
}
39+
40+
// Specializations for indexes, commonly used by unkeyed containers.
41+
@inline(__always)
42+
func appending(index: __owned Int) -> _CodingPathNode {
43+
.indexNode(index, self)
44+
}
45+
46+
func path(byAppendingIndex index: __owned Int) -> [CodingKey] {
47+
self.path + [_CodingKey(index: index)]
48+
}
49+
}
50+
51+
//===----------------------------------------------------------------------===//
52+
// Shared Key Type
53+
//===----------------------------------------------------------------------===//
54+
55+
internal enum _CodingKey : CodingKey {
56+
case string(String)
57+
case int(Int)
58+
case index(Int)
59+
60+
@inline(__always)
61+
public init?(stringValue: String) {
62+
self = .string(stringValue)
63+
}
64+
65+
@inline(__always)
66+
public init?(intValue: Int) {
67+
self = .int(intValue)
68+
}
69+
70+
@inline(__always)
71+
internal init(index: Int) {
72+
self = .index(index)
73+
}
74+
75+
var stringValue: String {
76+
switch self {
77+
case let .string(str): return str
78+
case let .int(int): return "\(int)"
79+
case let .index(index): return "Index \(index)"
80+
}
81+
}
82+
83+
var intValue: Int? {
84+
switch self {
85+
case .string: return nil
86+
case let .int(int): return int
87+
case let .index(index): return index
88+
}
89+
}
90+
}
91+
92+
struct JSONError: Error {
93+
var message: String
94+
init(message: String = "err") {
95+
self.message = message
96+
}
97+
}

0 commit comments

Comments
 (0)