Skip to content

Commit

Permalink
Add documentation to ProtobufMessage (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Ye authored Oct 30, 2024
1 parent c9a742f commit 19da4fe
Show file tree
Hide file tree
Showing 3 changed files with 527 additions and 6 deletions.
118 changes: 118 additions & 0 deletions Sources/OpenSwiftUICore/Data/Protobuf/ProtobufDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,41 @@

import Foundation

/// An object that decodes instances of a data type from protobuf objects.
package struct ProtobufDecoder {
/// An error that can occur during `ProtobufDecoder` decoding.
package enum DecodingError: Error {
case failed
}

/// A type representing a field in a protobuf encoding.
package typealias Field = ProtobufFormat.Field

/// A type representing the wire type of a protobuf encoding.
package typealias WireType = ProtobufFormat.WireType

/// The data being decoded.
var data: NSData

/// A pointer to the current position in the data.
var ptr: UnsafeRawPointer

/// A pointer to the end of the data.
var end: UnsafeRawPointer

/// The current packed field.
var packedField: Field = Field(rawValue: 0)

/// A pointer to the end of the packed field.
var packedEnd: UnsafeRawPointer

/// A stack of pointers for nested messages.
var stack: [UnsafeRawPointer] = []

/// User-defined information.
package var userInfo: [CodingUserInfoKey : Any] = [:]

/// Creates an instance with a data buffer.
package init(_ data: Data) {
let nsData = data as NSData
self.data = nsData
Expand All @@ -35,6 +54,7 @@ package struct ProtobufDecoder {
}

extension ProtobufDecoder {
/// Decodes the next field in the data.
package mutating func nextField() throws -> ProtobufDecoder.Field? {
guard ptr < end else {
packedField = Field(rawValue: 0)
Expand All @@ -55,6 +75,7 @@ extension ProtobufDecoder {
return field
}

/// Skips the next field in the data.
package mutating func skipField(_ field: ProtobufDecoder.Field) throws {
switch field.wireType {
case .varint:
Expand All @@ -78,6 +99,10 @@ extension ProtobufDecoder {
}
}

/// Decodes a boolean(Bool) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A boolean(Bool) value.
package mutating func boolField(_ field: ProtobufDecoder.Field) throws -> Bool {
switch field.wireType {
case .varint:
Expand All @@ -96,6 +121,10 @@ extension ProtobufDecoder {
return try decodeVariant() != 0
}

/// Decodes an unsigned integer(UInt) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: An unsigned integer(UInt) value.
package mutating func uintField(_ field: ProtobufDecoder.Field) throws -> UInt {
switch field.wireType {
case .varint:
Expand All @@ -114,31 +143,59 @@ extension ProtobufDecoder {
return try decodeVariant()
}

/// Decodes an enum field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A ProtobufEnum value.
package mutating func enumField<T>(_ field: ProtobufDecoder.Field) throws -> T? where T: ProtobufEnum {
try T(protobufValue: uintField(field))
}

/// Decodes an unsigned 8-bit integer(UInt8) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: An unsigned 8-bit integer(UInt8) value.
package mutating func uint8Field(_ field: ProtobufDecoder.Field) throws -> UInt8 {
try UInt8(uintField(field))
}

/// Decodes an unsigned 16-bit integer(UInt16) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: An unsigned 16-bit integer(UInt16) value.
package mutating func uint16Field(_ field: ProtobufDecoder.Field) throws -> UInt16 {
try UInt16(uintField(field))
}

/// Decodes an unsigned 32-bit integer(UInt32) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: An unsigned 32-bit integer(UInt32) value.
package mutating func uint32Field(_ field: ProtobufDecoder.Field) throws -> UInt32 {
try UInt32(uintField(field))
}

/// Decodes an unsigned 64-bit integer(UInt64) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: An unsigned 64-bit integer(UInt64) value.
package mutating func uint64Field(_ field: ProtobufDecoder.Field) throws -> UInt64 {
try UInt64(uintField(field))
}

/// Decodes a signed integer(Int) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A signed integer(Int) value.
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)))
}

/// Decodes a fixed 32-bit integer(UInt32) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A fixed 32-bit integer(UInt32) value.
package mutating func fixed32Field(_ field: ProtobufDecoder.Field) throws -> UInt32 {
switch field.wireType {
case .lengthDelimited:
Expand All @@ -163,6 +220,10 @@ extension ProtobufDecoder {
return value
}

/// Decodes a fixed 64-bit integer(UInt64) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A fixed 64-bit integer(UInt64) value.
package mutating func fixed64Field(_ field: ProtobufDecoder.Field) throws -> UInt64 {
switch field.wireType {
case .lengthDelimited:
Expand All @@ -187,6 +248,10 @@ extension ProtobufDecoder {
return value
}

/// Decodes a float(Float) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A float(Float) value.
package mutating func floatField(_ field: ProtobufDecoder.Field) throws -> Float {
switch field.wireType {
case .lengthDelimited:
Expand All @@ -211,6 +276,10 @@ extension ProtobufDecoder {
return Float(bitPattern: value)
}

/// Decodes a double(Double) value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A double(Double) value.
package mutating func doubleField(_ field: ProtobufDecoder.Field) throws -> Double {
switch field.wireType {
case .fixed64:
Expand Down Expand Up @@ -243,11 +312,19 @@ extension ProtobufDecoder {
return Double(bitPattern: value)
}

/// Decodes a CGFloat value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A CGFloat value.
@inline(__always)
package mutating func cgFloatField(_ field: ProtobufDecoder.Field) throws -> CGFloat {
try doubleField(field)
}

/// Decodes a data buffer value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A data buffer value.
package mutating func dataBufferField(_ field: ProtobufDecoder.Field) throws -> UnsafeRawBufferPointer {
switch field.wireType {
case .lengthDelimited:
Expand All @@ -257,6 +334,10 @@ extension ProtobufDecoder {
}
}

/// Decodes a data value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A data value.
package mutating func dataField(_ field: ProtobufDecoder.Field) throws -> Data {
switch field.wireType {
case .lengthDelimited:
Expand All @@ -272,20 +353,34 @@ extension ProtobufDecoder {
}
}

/// Decodes a message value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A ProtobufDecodableMessage value.
package mutating func messageField<T>(_ field: ProtobufDecoder.Field) throws -> T where T: ProtobufDecodableMessage {
guard field.wireType == .lengthDelimited else {
throw DecodingError.failed
}
return try decodeMessage()
}

/// Decodes a message value field from the data.
///
/// - Parameters:
/// - field: The field to decode.
/// - body: A closure that decodes the message.
/// - Returns: A value decoded from the message.
package mutating func messageField<T>(_ field: ProtobufDecoder.Field, _ body: (inout ProtobufDecoder) throws -> T) throws -> T {
guard field.wireType == .lengthDelimited else {
throw DecodingError.failed
}
return try decodeMessage(body)
}

/// Decodes a string value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A string value.
package mutating func stringField(_ field: ProtobufDecoder.Field) throws -> String {
let data = try dataField(field)
guard let result = String(data: data, encoding: .utf8) else {
Expand All @@ -294,13 +389,18 @@ extension ProtobufDecoder {
return result
}

/// Decodes a codable value field from the data.
///
/// - Parameter field: The field to decode.
/// - Returns: A codable value.
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 {
/// Decodes a variant from the data.
private mutating func decodeVariant() throws -> UInt {
var value: UInt = 0
var shift: UInt = 0
Expand All @@ -318,6 +418,7 @@ extension ProtobufDecoder {
return value
}

/// Decodes a data buffer from the data.
private mutating func decodeDataBuffer() throws -> UnsafeRawBufferPointer {
let count = try Int(decodeVariant())
let oldPtr = ptr
Expand All @@ -329,6 +430,7 @@ extension ProtobufDecoder {
return UnsafeRawBufferPointer(start: oldPtr, count: count)
}

/// Begins decoding a message.
private mutating func beginMessage() throws {
stack.append(end)
let count = try Int(decodeVariant())
Expand All @@ -339,18 +441,34 @@ extension ProtobufDecoder {
end = newPtr
}

/// Decodes a message from the data.
///
/// - Parameters:
/// - body: A closure that decodes the message.
/// - Returns: The value decoded from the message.
private mutating func decodeMessage<T>(_ body: (inout ProtobufDecoder) throws -> T) throws -> T {
try beginMessage()
defer { end = stack.removeLast() }
return try body(&self)
}

/// Decodes a message from the data.
///
/// - Returns: A ProtobufDecodableMessage value.
private mutating func decodeMessage<T>() throws -> T where T: ProtobufDecodableMessage {
try beginMessage()
defer { end = stack.removeLast() }
return try T(from: &self)
}

/// Decodes a value from a binary plist.
///
/// NOTE: This is only available on non-WASI platforms.
///
/// - Parameters:
/// - data: The plist data.
/// - type: The type to decode.
/// - Returns: The decodable value resulting from the plist data.
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")
Expand Down
Loading

0 comments on commit 19da4fe

Please sign in to comment.