11import Foundation
22
3- // MARK: - Postcard encoding helpers (subset for RPC)
3+ // MARK: - Primitive Encoding
4+
5+ /// Encode a boolean as postcard format (1 byte: 0 or 1)
6+ public func encodeBool( _ value: Bool ) -> [ UInt8 ] {
7+ return [ value ? 1 : 0 ]
8+ }
9+
10+ /// Encode an unsigned 8-bit integer (1 byte, no varint)
11+ public func encodeU8( _ value: UInt8 ) -> [ UInt8 ] {
12+ return [ value]
13+ }
14+
15+ /// Encode a signed 8-bit integer (1 byte)
16+ public func encodeI8( _ value: Int8 ) -> [ UInt8 ] {
17+ return [ UInt8 ( bitPattern: value) ]
18+ }
19+
20+ /// Encode an unsigned 16-bit integer as little-endian
21+ public func encodeU16( _ value: UInt16 ) -> [ UInt8 ] {
22+ return [ UInt8 ( value & 0xFF ) , UInt8 ( ( value >> 8 ) & 0xFF ) ]
23+ }
24+
25+ /// Encode a signed 16-bit integer as little-endian
26+ public func encodeI16( _ value: Int16 ) -> [ UInt8 ] {
27+ return encodeU16 ( UInt16 ( bitPattern: value) )
28+ }
29+
30+ /// Encode an unsigned 32-bit integer as varint
31+ public func encodeU32( _ value: UInt32 ) -> [ UInt8 ] {
32+ return encodeVarint ( UInt64 ( value) )
33+ }
34+
35+ /// Encode a signed 32-bit integer as zigzag varint
36+ public func encodeI32( _ value: Int32 ) -> [ UInt8 ] {
37+ // Zigzag encoding: (n << 1) ^ (n >> 31)
38+ let zigzag = UInt32 ( bitPattern: ( value << 1 ) ^ ( value >> 31 ) )
39+ return encodeVarint ( UInt64 ( zigzag) )
40+ }
41+
42+ /// Encode a signed 64-bit integer as zigzag varint
43+ public func encodeI64( _ value: Int64 ) -> [ UInt8 ] {
44+ // Zigzag encoding: (n << 1) ^ (n >> 63)
45+ let zigzag = UInt64 ( bitPattern: ( value << 1 ) ^ ( value >> 63 ) )
46+ return encodeVarint ( zigzag)
47+ }
48+
49+ /// Encode an unsigned 128-bit integer (16 bytes, little-endian)
50+ /// Note: Swift UInt128 is available from macOS 15.0+
51+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
52+ public func encodeU128( _ value: UInt128 ) -> [ UInt8 ] {
53+ var result = [ UInt8] ( repeating: 0 , count: 16 )
54+ var v = value
55+ for i in 0 ..< 16 {
56+ result [ i] = UInt8 ( truncatingIfNeeded: v)
57+ v >>= 8
58+ }
59+ return result
60+ }
61+
62+ /// Encode a signed 128-bit integer (16 bytes, little-endian)
63+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
64+ public func encodeI128( _ value: Int128 ) -> [ UInt8 ] {
65+ return encodeU128 ( UInt128 ( bitPattern: value) )
66+ }
67+
68+ /// Encode a 32-bit float (4 bytes, little-endian)
69+ public func encodeF32( _ value: Float ) -> [ UInt8 ] {
70+ let bits = value. bitPattern
71+ return [
72+ UInt8 ( bits & 0xFF ) ,
73+ UInt8 ( ( bits >> 8 ) & 0xFF ) ,
74+ UInt8 ( ( bits >> 16 ) & 0xFF ) ,
75+ UInt8 ( ( bits >> 24 ) & 0xFF ) ,
76+ ]
77+ }
78+
79+ /// Encode a 64-bit float (8 bytes, little-endian)
80+ public func encodeF64( _ value: Double ) -> [ UInt8 ] {
81+ let bits = value. bitPattern
82+ var result = [ UInt8] ( repeating: 0 , count: 8 )
83+ for i in 0 ..< 8 {
84+ result [ i] = UInt8 ( ( bits >> ( i * 8 ) ) & 0xFF )
85+ }
86+ return result
87+ }
488
589/// Encode a string as postcard format (length-prefixed UTF-8)
690public func encodeString( _ s: String ) -> [ UInt8 ] {
@@ -13,6 +97,144 @@ public func encodeBytes(_ bytes: [UInt8]) -> [UInt8] {
1397 return encodeVarint ( UInt64 ( bytes. count) ) + bytes
1498}
1599
100+ // MARK: - Container Encoding
101+
102+ /// Encode a vector/array with a length prefix
103+ public func encodeVec< T> ( _ items: [ T ] , encoder: ( T ) -> [ UInt8 ] ) -> [ UInt8 ] {
104+ var result = encodeVarint ( UInt64 ( items. count) )
105+ for item in items {
106+ result. append ( contentsOf: encoder ( item) )
107+ }
108+ return result
109+ }
110+
111+ /// Encode an optional value (0 for None, 1 + value for Some)
112+ public func encodeOption< T> ( _ value: T ? , encoder: ( T ) -> [ UInt8 ] ) -> [ UInt8 ] {
113+ if let v = value {
114+ return [ 1 ] + encoder( v)
115+ } else {
116+ return [ 0 ]
117+ }
118+ }
119+
120+ // MARK: - Primitive Decoding
121+
122+ /// Decode a boolean from postcard format
123+ public func decodeBool( from data: Data , offset: inout Int ) throws -> Bool {
124+ guard offset < data. count else {
125+ throw RoamError . decodeError ( " bool: unexpected EOF " )
126+ }
127+ let byte = data [ offset]
128+ offset += 1
129+ switch byte {
130+ case 0 : return false
131+ case 1 : return true
132+ default : throw RoamError . decodeError ( " bool: invalid value \( byte) " )
133+ }
134+ }
135+
136+ /// Decode an unsigned 8-bit integer
137+ public func decodeU8( from data: Data , offset: inout Int ) throws -> UInt8 {
138+ guard offset < data. count else {
139+ throw RoamError . decodeError ( " u8: unexpected EOF " )
140+ }
141+ let byte = data [ offset]
142+ offset += 1
143+ return byte
144+ }
145+
146+ /// Decode a signed 8-bit integer
147+ public func decodeI8( from data: Data , offset: inout Int ) throws -> Int8 {
148+ let u = try decodeU8 ( from: data, offset: & offset)
149+ return Int8 ( bitPattern: u)
150+ }
151+
152+ /// Decode an unsigned 16-bit integer (little-endian)
153+ public func decodeU16( from data: Data , offset: inout Int ) throws -> UInt16 {
154+ guard offset + 2 <= data. count else {
155+ throw RoamError . decodeError ( " u16: unexpected EOF " )
156+ }
157+ let result = UInt16 ( data [ offset] ) | ( UInt16 ( data [ offset + 1 ] ) << 8 )
158+ offset += 2
159+ return result
160+ }
161+
162+ /// Decode a signed 16-bit integer (little-endian)
163+ public func decodeI16( from data: Data , offset: inout Int ) throws -> Int16 {
164+ let u = try decodeU16 ( from: data, offset: & offset)
165+ return Int16 ( bitPattern: u)
166+ }
167+
168+ /// Decode an unsigned 32-bit integer (varint)
169+ public func decodeU32( from data: Data , offset: inout Int ) throws -> UInt32 {
170+ let v = try decodeVarint ( from: data, offset: & offset)
171+ guard v <= UInt64 ( UInt32 . max) else {
172+ throw RoamError . decodeError ( " u32: overflow " )
173+ }
174+ return UInt32 ( v)
175+ }
176+
177+ /// Decode a signed 32-bit integer (zigzag varint)
178+ public func decodeI32( from data: Data , offset: inout Int ) throws -> Int32 {
179+ let v = try decodeU32 ( from: data, offset: & offset)
180+ // Zigzag decode: (n >> 1) ^ -(n & 1)
181+ return Int32 ( bitPattern: ( v >> 1 ) ^ ( 0 &- ( v & 1 ) ) )
182+ }
183+
184+ /// Decode a signed 64-bit integer (zigzag varint)
185+ public func decodeI64( from data: Data , offset: inout Int ) throws -> Int64 {
186+ let v = try decodeVarint ( from: data, offset: & offset)
187+ // Zigzag decode: (n >> 1) ^ -(n & 1)
188+ return Int64 ( bitPattern: ( v >> 1 ) ^ ( 0 &- ( v & 1 ) ) )
189+ }
190+
191+ /// Decode an unsigned 128-bit integer (16 bytes, little-endian)
192+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
193+ public func decodeU128( from data: Data , offset: inout Int ) throws -> UInt128 {
194+ guard offset + 16 <= data. count else {
195+ throw RoamError . decodeError ( " u128: unexpected EOF " )
196+ }
197+ var result : UInt128 = 0
198+ for i in 0 ..< 16 {
199+ result |= UInt128 ( data [ offset + i] ) << ( i * 8 )
200+ }
201+ offset += 16
202+ return result
203+ }
204+
205+ /// Decode a signed 128-bit integer (16 bytes, little-endian)
206+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
207+ public func decodeI128( from data: Data , offset: inout Int ) throws -> Int128 {
208+ let u = try decodeU128 ( from: data, offset: & offset)
209+ return Int128 ( bitPattern: u)
210+ }
211+
212+ /// Decode a 32-bit float (4 bytes, little-endian)
213+ public func decodeF32( from data: Data , offset: inout Int ) throws -> Float {
214+ guard offset + 4 <= data. count else {
215+ throw RoamError . decodeError ( " f32: unexpected EOF " )
216+ }
217+ var bits : UInt32 = 0
218+ for i in 0 ..< 4 {
219+ bits |= UInt32 ( data [ offset + i] ) << ( i * 8 )
220+ }
221+ offset += 4
222+ return Float ( bitPattern: bits)
223+ }
224+
225+ /// Decode a 64-bit float (8 bytes, little-endian)
226+ public func decodeF64( from data: Data , offset: inout Int ) throws -> Double {
227+ guard offset + 8 <= data. count else {
228+ throw RoamError . decodeError ( " f64: unexpected EOF " )
229+ }
230+ var bits : UInt64 = 0
231+ for i in 0 ..< 8 {
232+ bits |= UInt64 ( data [ offset + i] ) << ( i * 8 )
233+ }
234+ offset += 8
235+ return Double ( bitPattern: bits)
236+ }
237+
16238/// Decode a string from postcard format
17239public func decodeString( from data: Data , offset: inout Int ) throws -> String {
18240 let length = try decodeVarint ( from: data, offset: & offset)
@@ -38,6 +260,49 @@ public func decodeBytes(from data: Data, offset: inout Int) throws -> Data {
38260 return bytes
39261}
40262
263+ // MARK: - Container Decoding
264+
265+ /// Decode a vector/array
266+ public func decodeVec< T> (
267+ from data: Data ,
268+ offset: inout Int ,
269+ decoder: ( Data , inout Int ) throws -> T
270+ ) throws -> [ T ] {
271+ let length = try decodeVarint ( from: data, offset: & offset)
272+ var result : [ T ] = [ ]
273+ result. reserveCapacity ( Int ( length) )
274+ for _ in 0 ..< length {
275+ result. append ( try decoder ( data, & offset) )
276+ }
277+ return result
278+ }
279+
280+ /// Decode an optional value
281+ public func decodeOption< T> (
282+ from data: Data ,
283+ offset: inout Int ,
284+ decoder: ( Data , inout Int ) throws -> T
285+ ) throws -> T ? {
286+ let tag = try decodeU8 ( from: data, offset: & offset)
287+ switch tag {
288+ case 0 : return nil
289+ case 1 : return try decoder ( data, & offset)
290+ default : throw RoamError . decodeError ( " option: invalid tag \( tag) " )
291+ }
292+ }
293+
294+ /// Decode a 2-tuple
295+ public func decodeTuple2< A, B> (
296+ from data: Data ,
297+ offset: inout Int ,
298+ decoderA: ( Data , inout Int ) throws -> A ,
299+ decoderB: ( Data , inout Int ) throws -> B
300+ ) throws -> ( A , B ) {
301+ let a = try decoderA ( data, & offset)
302+ let b = try decoderB ( data, & offset)
303+ return ( a, b)
304+ }
305+
41306// MARK: - Result encoding
42307
43308/// Encode Result::Ok variant
0 commit comments