Skip to content

Commit ab3f0e9

Browse files
authored
Add Protobuf encoder support (#143)
* Update enum encode case * Add basic Protobuf decode support * Add float test case and fix load alignment issue * Add data and packed decode support * Add string and codable decode support * Add protobuf message decode support * Add missing ProtobufMessage conformance * Fix WASI warning
1 parent fbf68cc commit ab3f0e9

File tree

8 files changed

+767
-77
lines changed

8 files changed

+767
-77
lines changed

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

Lines changed: 331 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
// OpenSwiftUICore
44
//
55
// Audited for RELEASE_2024
6-
// Status: WIP
6+
// Status: Complete
7+
// ID: FFA06CAF6B06DC3E21EC75547A0CD421
78

89
import Foundation
910

@@ -15,7 +16,6 @@ package struct ProtobufDecoder {
1516
package typealias Field = ProtobufFormat.Field
1617
package typealias WireType = ProtobufFormat.WireType
1718

18-
1919
var data: NSData
2020
var ptr: UnsafeRawPointer
2121
var end: UnsafeRawPointer
@@ -35,5 +35,333 @@ package struct ProtobufDecoder {
3535
}
3636

3737
extension ProtobufDecoder {
38-
// TODO: Implement decoding methods
38+
package mutating func nextField() throws -> ProtobufDecoder.Field? {
39+
guard ptr < end else {
40+
packedField = Field(rawValue: 0)
41+
return nil
42+
}
43+
if packedField.rawValue != 0 {
44+
if ptr < packedEnd {
45+
return packedField
46+
} else if packedEnd < ptr {
47+
throw DecodingError.failed
48+
}
49+
}
50+
let result = try decodeVariant()
51+
let field = Field(rawValue: result)
52+
guard field.tag > 0 else {
53+
throw DecodingError.failed
54+
}
55+
return field
56+
}
57+
58+
package mutating func skipField(_ field: ProtobufDecoder.Field) throws {
59+
switch field.wireType {
60+
case .varint:
61+
_ = try decodeVariant()
62+
case .fixed64:
63+
let newPtr = ptr.advanced(by: 8)
64+
guard newPtr <= end else {
65+
return
66+
}
67+
ptr = newPtr
68+
case .lengthDelimited:
69+
_ = try decodeDataBuffer()
70+
case .fixed32:
71+
let newPtr = ptr.advanced(by: 4)
72+
guard newPtr <= end else {
73+
return
74+
}
75+
ptr = newPtr
76+
default:
77+
throw DecodingError.failed
78+
}
79+
}
80+
81+
package mutating func boolField(_ field: ProtobufDecoder.Field) throws -> Bool {
82+
switch field.wireType {
83+
case .varint:
84+
break
85+
case .lengthDelimited:
86+
let offset = try decodeVariant()
87+
let offsetPtr = ptr.advanced(by: Int(offset))
88+
guard offsetPtr <= end else {
89+
throw DecodingError.failed
90+
}
91+
packedField = Field(field.tag, wireType: .varint)
92+
packedEnd = offsetPtr
93+
default:
94+
throw DecodingError.failed
95+
}
96+
return try decodeVariant() != 0
97+
}
98+
99+
package mutating func uintField(_ field: ProtobufDecoder.Field) throws -> UInt {
100+
switch field.wireType {
101+
case .varint:
102+
break
103+
case .lengthDelimited:
104+
let offset = try decodeVariant()
105+
let offsetPtr = ptr.advanced(by: Int(offset))
106+
guard offsetPtr <= end else {
107+
throw DecodingError.failed
108+
}
109+
packedField = Field(field.tag, wireType: .varint)
110+
packedEnd = offsetPtr
111+
default:
112+
throw DecodingError.failed
113+
}
114+
return try decodeVariant()
115+
}
116+
117+
package mutating func enumField<T>(_ field: ProtobufDecoder.Field) throws -> T? where T: ProtobufEnum {
118+
try T(protobufValue: uintField(field))
119+
}
120+
121+
package mutating func uint8Field(_ field: ProtobufDecoder.Field) throws -> UInt8 {
122+
try UInt8(uintField(field))
123+
}
124+
125+
package mutating func uint16Field(_ field: ProtobufDecoder.Field) throws -> UInt16 {
126+
try UInt16(uintField(field))
127+
}
128+
129+
package mutating func uint32Field(_ field: ProtobufDecoder.Field) throws -> UInt32 {
130+
try UInt32(uintField(field))
131+
}
132+
133+
package mutating func uint64Field(_ field: ProtobufDecoder.Field) throws -> UInt64 {
134+
try UInt64(uintField(field))
135+
}
136+
137+
package mutating func intField(_ field: ProtobufDecoder.Field) throws -> Int {
138+
let value = Int(bitPattern: try uintField(field))
139+
return Int(bitPattern: UInt(bitPattern: (value >> 1)) ^ UInt(bitPattern: -(value & 1)))
140+
}
141+
142+
package mutating func fixed32Field(_ field: ProtobufDecoder.Field) throws -> UInt32 {
143+
switch field.wireType {
144+
case .lengthDelimited:
145+
let offset = try decodeVariant()
146+
let offsetPtr = ptr.advanced(by: Int(offset))
147+
guard offsetPtr <= end else {
148+
throw DecodingError.failed
149+
}
150+
packedField = Field(field.tag, wireType: .fixed32)
151+
packedEnd = offsetPtr
152+
case .fixed32:
153+
break
154+
default:
155+
throw DecodingError.failed
156+
}
157+
let newPtr = ptr.advanced(by: 4)
158+
guard newPtr <= end else {
159+
throw DecodingError.failed
160+
}
161+
let value = ptr.loadUnaligned(as: UInt32.self)
162+
ptr = newPtr
163+
return value
164+
}
165+
166+
package mutating func fixed64Field(_ field: ProtobufDecoder.Field) throws -> UInt64 {
167+
switch field.wireType {
168+
case .lengthDelimited:
169+
let offset = try decodeVariant()
170+
let offsetPtr = ptr.advanced(by: Int(offset))
171+
guard offsetPtr <= end else {
172+
throw DecodingError.failed
173+
}
174+
packedField = Field(field.tag, wireType: .fixed64)
175+
packedEnd = offsetPtr
176+
case .fixed64:
177+
break
178+
default:
179+
throw DecodingError.failed
180+
}
181+
let newPtr = ptr.advanced(by: 8)
182+
guard newPtr <= end else {
183+
throw DecodingError.failed
184+
}
185+
let value = ptr.loadUnaligned(as: UInt64.self)
186+
ptr = newPtr
187+
return value
188+
}
189+
190+
package mutating func floatField(_ field: ProtobufDecoder.Field) throws -> Float {
191+
switch field.wireType {
192+
case .lengthDelimited:
193+
let offset = try decodeVariant()
194+
let offsetPtr = ptr.advanced(by: Int(offset))
195+
guard offsetPtr <= end else {
196+
throw DecodingError.failed
197+
}
198+
packedField = Field(field.tag, wireType: .fixed32)
199+
packedEnd = offsetPtr
200+
case .fixed32:
201+
break
202+
default:
203+
throw DecodingError.failed
204+
}
205+
let newPtr = ptr.advanced(by: 4)
206+
guard newPtr <= end else {
207+
throw DecodingError.failed
208+
}
209+
let value = ptr.loadUnaligned(as: UInt32.self)
210+
ptr = newPtr
211+
return Float(bitPattern: value)
212+
}
213+
214+
package mutating func doubleField(_ field: ProtobufDecoder.Field) throws -> Double {
215+
switch field.wireType {
216+
case .fixed64:
217+
break
218+
case .lengthDelimited:
219+
let offset = try decodeVariant()
220+
let offsetPtr = ptr.advanced(by: Int(offset))
221+
guard offsetPtr <= end else {
222+
throw DecodingError.failed
223+
}
224+
packedField = Field(field.tag, wireType: .fixed64)
225+
packedEnd = offsetPtr
226+
case .fixed32:
227+
let newPtr = ptr.advanced(by: 4)
228+
guard newPtr <= end else {
229+
throw DecodingError.failed
230+
}
231+
let value = ptr.loadUnaligned(as: UInt32.self)
232+
ptr = newPtr
233+
return Double(Float(bitPattern: value))
234+
default:
235+
throw DecodingError.failed
236+
}
237+
let newPtr = ptr.advanced(by: 8)
238+
guard newPtr <= end else {
239+
throw DecodingError.failed
240+
}
241+
let value = ptr.loadUnaligned(as: UInt64.self)
242+
ptr = newPtr
243+
return Double(bitPattern: value)
244+
}
245+
246+
@inline(__always)
247+
package mutating func cgFloatField(_ field: ProtobufDecoder.Field) throws -> CGFloat {
248+
try doubleField(field)
249+
}
250+
251+
package mutating func dataBufferField(_ field: ProtobufDecoder.Field) throws -> UnsafeRawBufferPointer {
252+
switch field.wireType {
253+
case .lengthDelimited:
254+
try decodeDataBuffer()
255+
default:
256+
throw DecodingError.failed
257+
}
258+
}
259+
260+
package mutating func dataField(_ field: ProtobufDecoder.Field) throws -> Data {
261+
switch field.wireType {
262+
case .lengthDelimited:
263+
let buffer = try decodeDataBuffer()
264+
guard let baseAddress = buffer.baseAddress else {
265+
return Data()
266+
}
267+
let startIndex = baseAddress - data.bytes
268+
let endIndex = startIndex + buffer.count
269+
return (data as Data)[startIndex..<endIndex]
270+
default:
271+
throw DecodingError.failed
272+
}
273+
}
274+
275+
package mutating func messageField<T>(_ field: ProtobufDecoder.Field) throws -> T where T: ProtobufDecodableMessage {
276+
guard field.wireType == .lengthDelimited else {
277+
throw DecodingError.failed
278+
}
279+
return try decodeMessage()
280+
}
281+
282+
package mutating func messageField<T>(_ field: ProtobufDecoder.Field, _ body: (inout ProtobufDecoder) throws -> T) throws -> T {
283+
guard field.wireType == .lengthDelimited else {
284+
throw DecodingError.failed
285+
}
286+
return try decodeMessage(body)
287+
}
288+
289+
package mutating func stringField(_ field: ProtobufDecoder.Field) throws -> String {
290+
let data = try dataField(field)
291+
guard let result = String(data: data, encoding: .utf8) else {
292+
throw DecodingError.failed
293+
}
294+
return result
295+
}
296+
297+
package mutating func codableField<T>(_ field: ProtobufDecoder.Field) throws -> T where T: Decodable {
298+
let data = try dataField(field)
299+
return try value(fromBinaryPlist: data)
300+
}
301+
}
302+
303+
extension ProtobufDecoder {
304+
private mutating func decodeVariant() throws -> UInt {
305+
var value: UInt = 0
306+
var shift: UInt = 0
307+
var shouldContinue = false
308+
repeat {
309+
guard ptr < end else {
310+
throw DecodingError.failed
311+
}
312+
let byte = ptr.loadUnaligned(as: UInt8.self)
313+
ptr += 1
314+
value |= UInt(byte & 0x7f) << shift
315+
shift += 7
316+
shouldContinue = (byte & 0x80 != 0)
317+
} while shouldContinue
318+
return value
319+
}
320+
321+
private mutating func decodeDataBuffer() throws -> UnsafeRawBufferPointer {
322+
let count = try Int(decodeVariant())
323+
let oldPtr = ptr
324+
let newPtr = ptr.advanced(by: count)
325+
guard newPtr <= end else {
326+
throw DecodingError.failed
327+
}
328+
ptr = newPtr
329+
return UnsafeRawBufferPointer(start: oldPtr, count: count)
330+
}
331+
332+
private mutating func beginMessage() throws {
333+
stack.append(end)
334+
let count = try Int(decodeVariant())
335+
let newPtr = ptr.advanced(by: count)
336+
guard newPtr <= end else {
337+
throw DecodingError.failed
338+
}
339+
end = newPtr
340+
}
341+
342+
private mutating func decodeMessage<T>(_ body: (inout ProtobufDecoder) throws -> T) throws -> T {
343+
try beginMessage()
344+
defer { end = stack.removeLast() }
345+
return try body(&self)
346+
}
347+
348+
private mutating func decodeMessage<T>() throws -> T where T: ProtobufDecodableMessage {
349+
try beginMessage()
350+
defer { end = stack.removeLast() }
351+
return try T(from: &self)
352+
}
353+
354+
func value<T>(fromBinaryPlist data: Data, type: T.Type = T.self) throws -> T where T: Decodable {
355+
#if os(WASI)
356+
fatalError("PropertyListDecoder is not avaiable on WASI")
357+
#else
358+
let decoder = PropertyListDecoder()
359+
decoder.userInfo = userInfo
360+
let resuls = try decoder.decode([T].self, from: data)
361+
guard let result = resuls.first else {
362+
throw DecodingError.failed
363+
}
364+
return result
365+
#endif
366+
}
39367
}

0 commit comments

Comments
 (0)