Skip to content

Add ProtobufMessage support #137

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

Merged
merged 3 commits into from
Oct 7, 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
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
}
}
Loading