Skip to content

Commit

Permalink
Add ProtobufMessage support (#137)
Browse files Browse the repository at this point in the history
* Update ProtobufMessage

* Update ProtobufEncoder and ProtobufDecoder

* Fix warning issue
  • Loading branch information
Kyle-Ye authored Oct 7, 2024
1 parent 0a00b95 commit 667fdbc
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 0 deletions.
39 changes: 39 additions & 0 deletions Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// ProtobufDecoder.swift
// OpenSwiftUICore
//
// Audited for RELEASE_2024
// Status: WIP

import Foundation

package struct ProtobufDecoder {
package enum DecodingError: Error {
case failed
}

package typealias Field = ProtobufFormat.Field
package typealias WireType = ProtobufFormat.WireType


var data: NSData
var ptr: UnsafeRawPointer
var end: UnsafeRawPointer
var packedField: Field = Field(rawValue: 0)
var packedEnd: UnsafeRawPointer
var stack: [UnsafeRawPointer] = []
package var userInfo: [CodingUserInfoKey : Any] = [:]

package init(_ data: Data) {
let nsData = data as NSData
self.data = nsData
let ptr = nsData.bytes
self.ptr = ptr
self.end = ptr + nsData.length
self.packedEnd = ptr
}
}

extension ProtobufDecoder {
// TODO: Implement decoding methods
}
44 changes: 44 additions & 0 deletions Sources/OpenSwiftUICore/Data/Protobuf/ProtobufEncoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// ProtobufEncoder.swift
// OpenSwiftUICore
//
// Audited for RELEASE_2024
// Status: WIP
// ID: C7B3AAD101AF9EA76FC322BD6EF713E6

import Foundation

package struct ProtobufEncoder {
package enum EncodingError: Error {
case failed
}
package typealias Field = ProtobufFormat.Field
package typealias WireType = ProtobufFormat.WireType

var buffer: UnsafeMutableRawPointer = .init(bitPattern: 0)!
var size: Int = 0
var capacity: Int = 0
var stack: [Int] = []
package var userInfo: [CodingUserInfoKey: Any] = [:]

package static func encoding(_ body: (inout ProtobufEncoder) throws -> Void) rethrows -> Data {
var encoder = ProtobufEncoder()
try body(&encoder)
defer { free(encoder.buffer) }
return encoder.takeData()
}

package static func encoding<T>(_ value: T) throws -> Data where T: ProtobufEncodableMessage {
try encoding { encoder in
try value.encode(to: &encoder)
}
}

private func takeData() -> Data {
Data(bytes: buffer, count: size)
}
}

extension ProtobufEncoder {
// TODO: Implement encoding methods
}
141 changes: 141 additions & 0 deletions Sources/OpenSwiftUICore/Data/Protobuf/ProtobufMessage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//
// ProtobufMessage.swift
// OpenSwiftUICore
//
// Audited for RELEASE_2024
// Status: Complete

import Foundation

// MARK: - ProtobufMessage

package protocol ProtobufEncodableMessage {
func encode(to encoder: inout ProtobufEncoder) throws
}
package protocol ProtobufDecodableMessage {
init(from decoder: inout ProtobufDecoder) throws
}

package typealias ProtobufMessage = ProtobufDecodableMessage & ProtobufEncodableMessage

// MARK: - ProtobufEnum

package protocol ProtobufEnum {
var protobufValue: UInt { get }
init?(protobufValue: UInt)
}

extension ProtobufEnum where Self: RawRepresentable, RawValue: BinaryInteger {
package var protobufValue: UInt {
UInt(rawValue)
}

package init?(protobufValue: UInt) {
self.init(rawValue: RawValue(protobufValue))
}
}

// MARK: - ProtobufTag

package protocol ProtobufTag: Equatable {
var rawValue: UInt { get }
init(rawValue: UInt)
}

// MARK: - ProtobufFormat

package enum ProtobufFormat {
package struct WireType: Equatable {
package let rawValue: UInt
package init(rawValue: UInt) {
self.rawValue = rawValue
}

package static var varint: ProtobufFormat.WireType { WireType(rawValue: 0) }
package static var fixed64: ProtobufFormat.WireType { WireType(rawValue: 1) }
package static var lengthDelimited: ProtobufFormat.WireType { WireType(rawValue: 2) }
package static var fixed32: ProtobufFormat.WireType { WireType(rawValue: 5) }
}

package struct Field: Equatable {
package var rawValue: UInt
package init(rawValue: UInt) {
self.rawValue = rawValue
}

// field = (field_number << 3) | wire_type
// See https://protobuf.dev/programming-guides/encoding/
package init(_ tag: UInt, wireType: WireType) {
rawValue = (tag << 3) | wireType.rawValue
}

package var tag: UInt {
rawValue >> 3
}

package var wireType: WireType {
WireType(rawValue: rawValue & 7)
}

@inline(__always)
package func tag<T>(as: T.Type = T.self) -> T where T: ProtobufTag {
T(rawValue: tag)
}
}
}

// MARK: - CoddleByProtobuf

package protocol CodaleByProtobuf: Codable, ProtobufMessage {}

extension CodaleByProtobuf {
func encode(to encoder: any Encoder) throws {
let data = try ProtobufEncoder.encoding { protobufEncoder in
protobufEncoder.userInfo = encoder.userInfo
try encode(to: &protobufEncoder)
}
var container = encoder.singleValueContainer()
try container.encode(data)
}

init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
var protobufDecoder = ProtobufDecoder(data)
protobufDecoder.userInfo = decoder.userInfo
self = try Self(from: &protobufDecoder)
}
}

// MARK: - ProtobufCodable

@propertyWrapper
package struct ProtobufCodable<Value>: Swift.Codable where Value: ProtobufMessage {
package var wrappedValue: Value
package init(wrappedValue: Value) {
self.wrappedValue = wrappedValue
}

package func encode(to encoder: any Encoder) throws {
let data = try ProtobufEncoder.encoding { protobufEncoder in
protobufEncoder.userInfo = encoder.userInfo
try wrappedValue.encode(to: &protobufEncoder)
}
var container = encoder.singleValueContainer()
try container.encode(data)
}

package init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
var protobufDecoder = ProtobufDecoder(data)
protobufDecoder.userInfo = decoder.userInfo
wrappedValue = try Value(from: &protobufDecoder)
}
}

extension ProtobufCodable: Equatable where Value: Equatable {
package static func == (lhs: ProtobufCodable<Value>, rhs: ProtobufCodable<Value>) -> Bool {
lhs.wrappedValue == rhs.wrappedValue
}
}

0 comments on commit 667fdbc

Please sign in to comment.