Skip to content
Open
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
165 changes: 155 additions & 10 deletions Sources/SwiftNetwork/Utilities/Deserializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public enum DeserializationResult: CustomStringConvertible, Equatable, Sendable
case success(parsedBytes: Int, remainingBytes: Int)
case error(DeserializationError)

@usableFromInline
static let success: Self = .success(parsedBytes: 0, remainingBytes: 0)

public var description: String {
Expand Down Expand Up @@ -76,11 +77,11 @@ public enum DeserializationResult: CustomStringConvertible, Equatable, Sendable
@available(Network 0.1.0, *)
public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escapable>: ~Copyable, ~Escapable {
private var factory: Factory
private var currentSpan: RawSpan
private var currentSpanByteCount = 0
@usableFromInline internal var currentSpan: RawSpan
@usableFromInline internal var currentSpanByteCount = 0
private var availableByteCount: Int
private var scratchSpace: [16 of UInt8]? // Initialized lazily
private var cursor = 0
@usableFromInline internal var cursor = 0
private var previousSpanAggregateByteCount = 0
private(set) var internalResult: DeserializationResult = .success

Expand Down Expand Up @@ -339,11 +340,32 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
moveCursorUnchecked(length)
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint8(_ value: inout UInt8) throws(DeserializationError) {
try readFixedSize(&value)
guard cursor < currentSpanByteCount else {
// Fallback for multi-span cases
try readUInt8AtSpanBoundary(&value)
return
}
value = UInt8(currentSpan.unsafeLoadUnaligned(fromByteOffset: cursor, as: UInt8.self))
// Move by one byte
cursor &+= MemoryLayout<UInt8>.size
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint8(_ value: inout UInt8?) throws(DeserializationError) {
var temp: UInt8 = 0
try uint8(&temp)
value = temp
}

@usableFromInline
@inline(never)
internal mutating func readUInt8AtSpanBoundary(_ value: inout UInt8) throws(DeserializationError) {
try readFixedSize(&value)
}

Expand All @@ -363,11 +385,31 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
try validateValue(expect: value)
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint16(_ value: inout UInt16) throws(DeserializationError) {
try readFixedSize(&value)
guard (currentSpanByteCount - cursor) >= MemoryLayout<UInt16>.size else {
// Fallback for multi-span cases
try readUInt16AtSpanBoundary(&value)
return
}
value = currentSpan.unsafeLoadUnaligned(fromByteOffset: cursor, as: UInt16.self)
cursor &+= MemoryLayout<UInt16>.size
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint16(_ value: inout UInt16?) throws(DeserializationError) {
var temp: UInt16 = 0
try uint16(&temp)
value = temp
}

@usableFromInline
@inline(never)
internal mutating func readUInt16AtSpanBoundary(_ value: inout UInt16) throws(DeserializationError) {
try readFixedSize(&value)
}

Expand All @@ -387,59 +429,160 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
try validateValue(expect: value)
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint32(_ value: inout UInt32) throws(DeserializationError) {
try readFixedSize(&value)
guard (currentSpanByteCount - cursor) >= MemoryLayout<UInt32>.size else {
// Fallback for multi-span cases
try readUInt32AtSpanBoundary(&value)
return
}
value = currentSpan.unsafeLoadUnaligned(fromByteOffset: cursor, as: UInt32.self)
cursor &+= MemoryLayout<UInt32>.size
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint32(_ value: inout UInt32?) throws(DeserializationError) {
var temp: UInt32 = 0
try uint32(&temp)
value = temp
}

@usableFromInline
@inline(never)
internal mutating func readUInt32AtSpanBoundary(_ value: inout UInt32) throws(DeserializationError) {
try readFixedSize(&value)
}

public mutating func uint32(expect value: UInt32) throws(DeserializationError) {
try validateValue(expect: value)
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint64(_ value: inout UInt64) throws(DeserializationError) {
try readFixedSize(&value)
guard (currentSpanByteCount - cursor) >= MemoryLayout<UInt64>.size else {
// Fallback for multi-span cases
try readUInt64AtSpanBoundary(&value)
return
}
value = currentSpan.unsafeLoadUnaligned(fromByteOffset: cursor, as: UInt64.self)
// Move by eight bytes
cursor &+= MemoryLayout<UInt64>.size
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint64(_ value: inout UInt64?) throws(DeserializationError) {
var temp: UInt64 = 0
try uint64(&temp)
value = temp
}

@usableFromInline
@inline(never)
internal mutating func readUInt64AtSpanBoundary(_ value: inout UInt64) throws(DeserializationError) {
try readFixedSize(&value)
}

public mutating func uint64(expect value: UInt64) throws(DeserializationError) {
try validateValue(expect: value)
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint16NetworkByteOrder(_ value: inout UInt16) throws(DeserializationError) {
try readFixedSize(&value, networkByteOrder: true)
guard (currentSpanByteCount - cursor) >= MemoryLayout<UInt16>.size else {
// Fallback for multi-span cases
try readUInt16NBOAtSpanBoundary(&value)
return
}
value = UInt16(bigEndian: currentSpan.unsafeLoadUnaligned(fromByteOffset: cursor, as: UInt16.self))
cursor &+= MemoryLayout<UInt16>.size
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint16NetworkByteOrder(_ value: inout UInt16?) throws(DeserializationError) {
var temp: UInt16 = 0
try uint16NetworkByteOrder(&temp)
value = temp
}

@usableFromInline
@inline(never)
internal mutating func readUInt16NBOAtSpanBoundary(_ value: inout UInt16) throws(DeserializationError) {
try readFixedSize(&value, networkByteOrder: true)
}

public mutating func uint16NetworkByteOrder(expect value: UInt16) throws(DeserializationError) {
try validateValue(expect: value.bigEndian)
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint32NetworkByteOrder(_ value: inout UInt32) throws(DeserializationError) {
try readFixedSize(&value, networkByteOrder: true)
guard (currentSpanByteCount - cursor) >= MemoryLayout<UInt32>.size else {
// Fallback for multi-span cases
try readUInt32NBOAtSpanBoundary(&value)
return
}
value = UInt32(bigEndian: currentSpan.unsafeLoadUnaligned(fromByteOffset: cursor, as: UInt32.self))
cursor &+= MemoryLayout<UInt32>.size
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint32NetworkByteOrder(_ value: inout UInt32?) throws(DeserializationError) {
var temp: UInt32 = 0
try uint32NetworkByteOrder(&temp)
value = temp
}

@usableFromInline
@inline(never)
internal mutating func readUInt32NBOAtSpanBoundary(_ value: inout UInt32) throws(DeserializationError) {
try readFixedSize(&value, networkByteOrder: true)
}

public mutating func uint32NetworkByteOrder(expect value: UInt32) throws(DeserializationError) {
try validateValue(expect: value.bigEndian)
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint64NetworkByteOrder(_ value: inout UInt64) throws(DeserializationError) {
try readFixedSize(&value, networkByteOrder: true)
guard (currentSpanByteCount - cursor) >= MemoryLayout<UInt64>.size else {
// Fallback for multi-span cases
try readUInt64NBOAtSpanBoundary(&value)
return
}
value = UInt64(bigEndian: currentSpan.unsafeLoadUnaligned(fromByteOffset: cursor, as: UInt64.self))
cursor &+= MemoryLayout<UInt64>.size
}

@_optimize(speed)
@inlinable
@inline(__always)
public mutating func uint64NetworkByteOrder(_ value: inout UInt64?) throws(DeserializationError) {
var temp: UInt64 = 0
try uint64NetworkByteOrder(&temp)
value = temp
}

@usableFromInline
@inline(never)
internal mutating func readUInt64NBOAtSpanBoundary(_ value: inout UInt64) throws(DeserializationError) {
try readFixedSize(&value, networkByteOrder: true)
}

Expand Down Expand Up @@ -827,6 +970,7 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap
}

@_optimize(speed)
@inlinable
public static func deserialize(
_ frame: inout Frame,
claim: Bool,
Expand All @@ -846,6 +990,7 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap

#if !NETWORK_EMBEDDED
@_optimize(speed)
@inlinable
public static func deserialize(
_ frameArray: inout FrameArray,
claim: Bool,
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftNetwork/Utilities/SerializationHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public struct FrameArraySpanFactory: ~Copyable, ~Escapable, DeserializerSpanFact
public private(set) var availableByteCount: Int

/// Extracts the frame array from the factory, consuming the factory in the process.
@usableFromInline
consuming func takeFrameArray() -> FrameArray {
frameArray
}
Expand All @@ -119,6 +120,7 @@ public struct FrameArraySpanFactory: ~Copyable, ~Escapable, DeserializerSpanFact
// lifetime dependency. @_lifetime(immortal) is correct for a self-contained
// ~Escapable type whose data is fully owned.
@_lifetime(immortal)
@usableFromInline
init(_ frameArray: consuming FrameArray) {
self.spanCount = frameArray.count
self.availableByteCount = frameArray.unclaimedLength
Expand Down