Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Protobuf encoder support #143

Merged
merged 8 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 331 additions & 3 deletions Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// OpenSwiftUICore
//
// Audited for RELEASE_2024
// Status: WIP
// Status: Complete
// ID: FFA06CAF6B06DC3E21EC75547A0CD421

import Foundation

Expand All @@ -15,7 +16,6 @@
package typealias Field = ProtobufFormat.Field
package typealias WireType = ProtobufFormat.WireType


var data: NSData
var ptr: UnsafeRawPointer
var end: UnsafeRawPointer
Expand All @@ -35,5 +35,333 @@
}

extension ProtobufDecoder {
// TODO: Implement decoding methods
package mutating func nextField() throws -> ProtobufDecoder.Field? {
guard ptr < end else {
packedField = Field(rawValue: 0)
return nil
}
if packedField.rawValue != 0 {
if ptr < packedEnd {
return packedField
} else if packedEnd < ptr {
throw DecodingError.failed

Check warning on line 47 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L47

Added line #L47 was not covered by tests
}
}
let result = try decodeVariant()
let field = Field(rawValue: result)
guard field.tag > 0 else {
throw DecodingError.failed

Check warning on line 53 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L53

Added line #L53 was not covered by tests
}
return field
}

package mutating func skipField(_ field: ProtobufDecoder.Field) throws {
switch field.wireType {
case .varint:
_ = try decodeVariant()
case .fixed64:
let newPtr = ptr.advanced(by: 8)
guard newPtr <= end else {
return

Check warning on line 65 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L63-L65

Added lines #L63 - L65 were not covered by tests
}
ptr = newPtr

Check warning on line 67 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L67

Added line #L67 was not covered by tests
case .lengthDelimited:
_ = try decodeDataBuffer()

Check warning on line 69 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L69

Added line #L69 was not covered by tests
case .fixed32:
let newPtr = ptr.advanced(by: 4)
guard newPtr <= end else {
return

Check warning on line 73 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L71-L73

Added lines #L71 - L73 were not covered by tests
}
ptr = newPtr

Check warning on line 75 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L75

Added line #L75 was not covered by tests
default:
throw DecodingError.failed

Check warning on line 77 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L77

Added line #L77 was not covered by tests
}
}

package mutating func boolField(_ field: ProtobufDecoder.Field) throws -> Bool {
switch field.wireType {
case .varint:
break
case .lengthDelimited:
let offset = try decodeVariant()
let offsetPtr = ptr.advanced(by: Int(offset))
guard offsetPtr <= end else {
throw DecodingError.failed

Check warning on line 89 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L86-L89

Added lines #L86 - L89 were not covered by tests
}
packedField = Field(field.tag, wireType: .varint)
packedEnd = offsetPtr

Check warning on line 92 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L91-L92

Added lines #L91 - L92 were not covered by tests
default:
throw DecodingError.failed

Check warning on line 94 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L94

Added line #L94 was not covered by tests
}
return try decodeVariant() != 0
}

package mutating func uintField(_ field: ProtobufDecoder.Field) throws -> UInt {
switch field.wireType {
case .varint:
break
case .lengthDelimited:
let offset = try decodeVariant()
let offsetPtr = ptr.advanced(by: Int(offset))
guard offsetPtr <= end else {
throw DecodingError.failed

Check warning on line 107 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L107

Added line #L107 was not covered by tests
}
packedField = Field(field.tag, wireType: .varint)
packedEnd = offsetPtr
default:
throw DecodingError.failed

Check warning on line 112 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L112

Added line #L112 was not covered by tests
}
return try decodeVariant()
}

package mutating func enumField<T>(_ field: ProtobufDecoder.Field) throws -> T? where T: ProtobufEnum {
try T(protobufValue: uintField(field))
}

package mutating func uint8Field(_ field: ProtobufDecoder.Field) throws -> UInt8 {
try UInt8(uintField(field))

Check warning on line 122 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L121-L122

Added lines #L121 - L122 were not covered by tests
}

package mutating func uint16Field(_ field: ProtobufDecoder.Field) throws -> UInt16 {
try UInt16(uintField(field))

Check warning on line 126 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L125-L126

Added lines #L125 - L126 were not covered by tests
}

package mutating func uint32Field(_ field: ProtobufDecoder.Field) throws -> UInt32 {
try UInt32(uintField(field))

Check warning on line 130 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L129-L130

Added lines #L129 - L130 were not covered by tests
}

package mutating func uint64Field(_ field: ProtobufDecoder.Field) throws -> UInt64 {
try UInt64(uintField(field))
}

package mutating func intField(_ field: ProtobufDecoder.Field) throws -> Int {
let value = Int(bitPattern: try uintField(field))
return Int(bitPattern: UInt(bitPattern: (value >> 1)) ^ UInt(bitPattern: -(value & 1)))
}

package mutating func fixed32Field(_ field: ProtobufDecoder.Field) throws -> UInt32 {
switch field.wireType {
case .lengthDelimited:
let offset = try decodeVariant()
let offsetPtr = ptr.advanced(by: Int(offset))
guard offsetPtr <= end else {
throw DecodingError.failed

Check warning on line 148 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L145-L148

Added lines #L145 - L148 were not covered by tests
}
packedField = Field(field.tag, wireType: .fixed32)
packedEnd = offsetPtr

Check warning on line 151 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L150-L151

Added lines #L150 - L151 were not covered by tests
case .fixed32:
break
default:
throw DecodingError.failed

Check warning on line 155 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L155

Added line #L155 was not covered by tests
}
let newPtr = ptr.advanced(by: 4)
guard newPtr <= end else {
throw DecodingError.failed

Check warning on line 159 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L159

Added line #L159 was not covered by tests
}
let value = ptr.loadUnaligned(as: UInt32.self)
ptr = newPtr
return value
}

package mutating func fixed64Field(_ field: ProtobufDecoder.Field) throws -> UInt64 {
switch field.wireType {
case .lengthDelimited:
let offset = try decodeVariant()
let offsetPtr = ptr.advanced(by: Int(offset))
guard offsetPtr <= end else {
throw DecodingError.failed

Check warning on line 172 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L166-L172

Added lines #L166 - L172 were not covered by tests
}
packedField = Field(field.tag, wireType: .fixed64)
packedEnd = offsetPtr
case .fixed64:
break
default:
throw DecodingError.failed

Check warning on line 179 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L174-L179

Added lines #L174 - L179 were not covered by tests
}
let newPtr = ptr.advanced(by: 8)
guard newPtr <= end else {
throw DecodingError.failed

Check warning on line 183 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L181-L183

Added lines #L181 - L183 were not covered by tests
}
let value = ptr.loadUnaligned(as: UInt64.self)
ptr = newPtr
return value

Check warning on line 187 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L185-L187

Added lines #L185 - L187 were not covered by tests
}

package mutating func floatField(_ field: ProtobufDecoder.Field) throws -> Float {
switch field.wireType {
case .lengthDelimited:
let offset = try decodeVariant()
let offsetPtr = ptr.advanced(by: Int(offset))
guard offsetPtr <= end else {
throw DecodingError.failed

Check warning on line 196 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L193-L196

Added lines #L193 - L196 were not covered by tests
}
packedField = Field(field.tag, wireType: .fixed32)
packedEnd = offsetPtr

Check warning on line 199 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L198-L199

Added lines #L198 - L199 were not covered by tests
case .fixed32:
break
default:
throw DecodingError.failed

Check warning on line 203 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L203

Added line #L203 was not covered by tests
}
let newPtr = ptr.advanced(by: 4)
guard newPtr <= end else {
throw DecodingError.failed

Check warning on line 207 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L207

Added line #L207 was not covered by tests
}
let value = ptr.loadUnaligned(as: UInt32.self)
ptr = newPtr
return Float(bitPattern: value)
}

package mutating func doubleField(_ field: ProtobufDecoder.Field) throws -> Double {
switch field.wireType {
case .fixed64:
break
case .lengthDelimited:
let offset = try decodeVariant()
let offsetPtr = ptr.advanced(by: Int(offset))
guard offsetPtr <= end else {
throw DecodingError.failed

Check warning on line 222 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L219-L222

Added lines #L219 - L222 were not covered by tests
}
packedField = Field(field.tag, wireType: .fixed64)
packedEnd = offsetPtr

Check warning on line 225 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L224-L225

Added lines #L224 - L225 were not covered by tests
case .fixed32:
let newPtr = ptr.advanced(by: 4)
guard newPtr <= end else {
throw DecodingError.failed

Check warning on line 229 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L229

Added line #L229 was not covered by tests
}
let value = ptr.loadUnaligned(as: UInt32.self)
ptr = newPtr
return Double(Float(bitPattern: value))
default:
throw DecodingError.failed

Check warning on line 235 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L235

Added line #L235 was not covered by tests
}
let newPtr = ptr.advanced(by: 8)
guard newPtr <= end else {
throw DecodingError.failed

Check warning on line 239 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L239

Added line #L239 was not covered by tests
}
let value = ptr.loadUnaligned(as: UInt64.self)
ptr = newPtr
return Double(bitPattern: value)
}

@inline(__always)
package mutating func cgFloatField(_ field: ProtobufDecoder.Field) throws -> CGFloat {
try doubleField(field)
}

package mutating func dataBufferField(_ field: ProtobufDecoder.Field) throws -> UnsafeRawBufferPointer {
switch field.wireType {
case .lengthDelimited:
try decodeDataBuffer()
default:
throw DecodingError.failed

Check warning on line 256 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L251-L256

Added lines #L251 - L256 were not covered by tests
}
}

package mutating func dataField(_ field: ProtobufDecoder.Field) throws -> Data {
switch field.wireType {
case .lengthDelimited:
let buffer = try decodeDataBuffer()
guard let baseAddress = buffer.baseAddress else {
return Data()

Check warning on line 265 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L265

Added line #L265 was not covered by tests
}
let startIndex = baseAddress - data.bytes
let endIndex = startIndex + buffer.count
return (data as Data)[startIndex..<endIndex]
default:
throw DecodingError.failed

Check warning on line 271 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L271

Added line #L271 was not covered by tests
}
}

package mutating func messageField<T>(_ field: ProtobufDecoder.Field) throws -> T where T: ProtobufDecodableMessage {
guard field.wireType == .lengthDelimited else {
throw DecodingError.failed

Check warning on line 277 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L277

Added line #L277 was not covered by tests
}
return try decodeMessage()
}

package mutating func messageField<T>(_ field: ProtobufDecoder.Field, _ body: (inout ProtobufDecoder) throws -> T) throws -> T {
guard field.wireType == .lengthDelimited else {
throw DecodingError.failed

Check warning on line 284 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L282-L284

Added lines #L282 - L284 were not covered by tests
}
return try decodeMessage(body)

Check warning on line 286 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L286

Added line #L286 was not covered by tests
}

package mutating func stringField(_ field: ProtobufDecoder.Field) throws -> String {
let data = try dataField(field)
guard let result = String(data: data, encoding: .utf8) else {
throw DecodingError.failed

Check warning on line 292 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L292

Added line #L292 was not covered by tests
}
return result
}

package mutating func codableField<T>(_ field: ProtobufDecoder.Field) throws -> T where T: Decodable {
let data = try dataField(field)
return try value(fromBinaryPlist: data)
}
}

extension ProtobufDecoder {
private mutating func decodeVariant() throws -> UInt {
var value: UInt = 0
var shift: UInt = 0
var shouldContinue = false
repeat {
guard ptr < end else {
throw DecodingError.failed

Check warning on line 310 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L310

Added line #L310 was not covered by tests
}
let byte = ptr.loadUnaligned(as: UInt8.self)
ptr += 1
value |= UInt(byte & 0x7f) << shift
shift += 7
shouldContinue = (byte & 0x80 != 0)
} while shouldContinue
return value
}

private mutating func decodeDataBuffer() throws -> UnsafeRawBufferPointer {
let count = try Int(decodeVariant())
let oldPtr = ptr
let newPtr = ptr.advanced(by: count)
guard newPtr <= end else {
throw DecodingError.failed

Check warning on line 326 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L326

Added line #L326 was not covered by tests
}
ptr = newPtr
return UnsafeRawBufferPointer(start: oldPtr, count: count)
}

private mutating func beginMessage() throws {
stack.append(end)
let count = try Int(decodeVariant())
let newPtr = ptr.advanced(by: count)
guard newPtr <= end else {
throw DecodingError.failed

Check warning on line 337 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L337

Added line #L337 was not covered by tests
}
end = newPtr
}

private mutating func decodeMessage<T>(_ body: (inout ProtobufDecoder) throws -> T) throws -> T {
try beginMessage()
defer { end = stack.removeLast() }
return try body(&self)

Check warning on line 345 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L342-L345

Added lines #L342 - L345 were not covered by tests
}

private mutating func decodeMessage<T>() throws -> T where T: ProtobufDecodableMessage {
try beginMessage()
defer { end = stack.removeLast() }
return try T(from: &self)
}

func value<T>(fromBinaryPlist data: Data, type: T.Type = T.self) throws -> T where T: Decodable {
#if os(WASI)
fatalError("PropertyListDecoder is not avaiable on WASI")
#else
let decoder = PropertyListDecoder()
decoder.userInfo = userInfo
let resuls = try decoder.decode([T].self, from: data)
guard let result = resuls.first else {
throw DecodingError.failed

Check warning on line 362 in Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift#L362

Added line #L362 was not covered by tests
}
return result
#endif
}
}
Loading
Loading