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 documentation to ProtobufMessage #148

Merged
merged 1 commit into from
Oct 30, 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
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
Loading