• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

subpop / swift-dbus / 20435689450

22 Dec 2025 03:04PM UTC coverage: 51.766% (-0.06%) from 51.826%
20435689450

push

github

subpop
Bump github.com/apple/swift-argument-parser from 1.6.2 to 1.7.0

Bumps [github.com/apple/swift-argument-parser](https://github.com/apple/swift-argument-parser) from 1.6.2 to 1.7.0.
- [Release notes](https://github.com/apple/swift-argument-parser/releases)
- [Commits](https://github.com/apple/swift-argument-parser/compare/1.6.2...1.7.0)

---
updated-dependencies:
- dependency-name: github.com/apple/swift-argument-parser
  dependency-version: 1.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

3459 of 6682 relevant lines covered (51.77%)

643283.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.54
/Sources/DBus/Serializer.swift
1
//
2
// Copyright 2025 Link Dupont
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License");
5
// you may not use this file except in compliance with the License.
6
// You may obtain a copy of the License at
7
//
8
//     http://www.apache.org/licenses/LICENSE-2.0
9
//
10
// Unless required by applicable law or agreed to in writing, software
11
// distributed under the License is distributed on an "AS IS" BASIS,
12
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
// See the License for the specific language governing permissions and
14
// limitations under the License.
15
//
16

17
/// Alignment context for controlling when elements should be aligned
18
public enum AlignmentContext {
19
    case message  // Top-level message elements (apply alignment)
20
    case structContent  // Inside struct (no individual element alignment)
21
}
22

23
/// `Serializer` converts native Swift types into a format compatible with the D-Bus
24
/// wire format. No assumptions are made about how to encode native Swift types. A signature
25
/// must be given to a serializer upon initialization. This signature is used to ensure serialization
26
/// of the expected types is strictly adhered to. It is an error to serialize a value other than what
27
/// is expected next according to the signature.
28
public struct Serializer {
29
    var signature: Signature
30
    var endianness: Endianness
31

32
    private var alignment: Int = 0
272✔
33
    private var _data: [UInt8] = []
272✔
34
    private var currentIndex: Signature.Index
35
    private var alignmentContext: AlignmentContext
36

37
    /// The final, marshaled data. This property will be `nil` unless all fields defined in the
38
    /// serializer's signature have been serialized.
39
    public var data: [UInt8]? {
272✔
40
        guard currentIndex >= signature.endIndex else {
272✔
41
            return nil
×
42
        }
272✔
43
        return _data
272✔
44
    }
272✔
45

46
    /// Create a new `Serializer`, configured with the given `Signature`.
47
    public init(
48
        signature: Signature, alignmentContext: AlignmentContext = .message,
49
        endianness: Endianness = .littleEndian
50
    ) {
272✔
51
        self.signature = signature
272✔
52
        self.currentIndex = signature.startIndex
272✔
53
        self.alignmentContext = alignmentContext
272✔
54
        self.endianness = endianness
272✔
55
    }
272✔
56

57
    /// Get the current expected signature element
58
    public var currentSignatureElement: SignatureElement {
×
59
        signature[currentIndex]
×
60
    }
×
61

62
    /// Marshal `Bool` value into bytes.
63
    private func marshal(_ value: Bool) throws -> [UInt8] {
22✔
64
        let boolValue: UInt32 = value ? 1 : 0
22✔
65
        return try byteSize(boolValue)
22✔
66
    }
22✔
67

68
    /// Encode `value` as a `Bool`.
69
    public mutating func serialize(_ value: Bool) throws {
7✔
70
        guard signature[currentIndex] == .bool else {
7✔
71
            throw
×
72
                SerializerError
×
73
                .signatureElementMismatch(
×
74
                    gotElement: signature[currentIndex],
×
75
                    forType: Bool.self)
×
76
        }
7✔
77
        defer { currentIndex += 1 }
7✔
78

7✔
79
        if alignmentContext == .message {
7✔
80
            pad(to: signature[currentIndex].alignment)
7✔
81
        }
7✔
82
        _data.append(contentsOf: try marshal(value))
7✔
83
    }
7✔
84

85
    /// Marshal `UInt8` value into bytes.
86
    private func marshal(_ value: UInt8) throws -> [UInt8] {
19✔
87
        var data: [UInt8] = []
19✔
88
        data.append(contentsOf: try byteSize(value))
19✔
89
        return data
19✔
90
    }
19✔
91

92
    /// Encode `value` as a `UInt8`.
93
    public mutating func serialize(_ value: UInt8) throws {
2✔
94
        guard signature[currentIndex] == .byte else {
2✔
95
            throw
×
96
                SerializerError
×
97
                .signatureElementMismatch(
×
98
                    gotElement: signature[currentIndex],
×
99
                    forType: UInt8.self)
×
100
        }
2✔
101
        defer { currentIndex += 1 }
2✔
102

2✔
103
        if alignmentContext == .message {
2✔
104
            pad(to: signature[currentIndex].alignment)
2✔
105
        }
2✔
106
        _data.append(contentsOf: try marshal(value))
2✔
107
    }
2✔
108

109
    /// Marshal `UInt16` value into bytes.
110
    private func marshal(_ value: UInt16) throws -> [UInt8] {
6✔
111
        var data: [UInt8] = []
6✔
112
        data.append(contentsOf: try byteSize(value))
6✔
113
        return data
6✔
114
    }
6✔
115

116
    /// Encode `value` as a `UInt16`.
117
    public mutating func serialize(_ value: UInt16) throws {
2✔
118
        guard signature[currentIndex] == .uint16 else {
2✔
119
            throw
×
120
                SerializerError
×
121
                .signatureElementMismatch(
×
122
                    gotElement: signature[currentIndex],
×
123
                    forType: UInt16.self)
×
124
        }
2✔
125
        defer { currentIndex += 1 }
2✔
126

2✔
127
        if alignmentContext == .message {
2✔
128
            pad(to: signature[currentIndex].alignment)
2✔
129
        }
2✔
130
        _data.append(contentsOf: try marshal(value))
2✔
131
    }
2✔
132

133
    /// Marshal `UInt32` value into bytes.
134
    private func marshal(_ value: UInt32) throws -> [UInt8] {
20✔
135
        var data: [UInt8] = []
20✔
136
        data.append(contentsOf: try byteSize(value))
20✔
137
        return data
20✔
138
    }
20✔
139

140
    /// Encode `value` as a `UInt32`.
141
    public mutating func serialize(_ value: UInt32) throws {
8✔
142
        guard signature[currentIndex] == .uint32 else {
8✔
143
            throw
×
144
                SerializerError
×
145
                .signatureElementMismatch(
×
146
                    gotElement: signature[currentIndex],
×
147
                    forType: UInt32.self)
×
148
        }
8✔
149
        defer { currentIndex += 1 }
8✔
150

8✔
151
        if alignmentContext == .message {
8✔
152
            pad(to: signature[currentIndex].alignment)
6✔
153
        }
6✔
154
        _data.append(contentsOf: try marshal(value))
8✔
155
    }
8✔
156

157
    /// Marshal `UInt64` value into bytes.
158
    private func marshal(_ value: UInt64) throws -> [UInt8] {
8✔
159
        var data: [UInt8] = []
8✔
160
        data.append(contentsOf: try byteSize(value))
8✔
161
        return data
8✔
162
    }
8✔
163

164
    /// Encode `value` as a `UInt64`.
165
    public mutating func serialize(_ value: UInt64) throws {
4✔
166
        guard signature[currentIndex] == .uint64 else {
4✔
167
            throw
×
168
                SerializerError
×
169
                .signatureElementMismatch(
×
170
                    gotElement: signature[currentIndex],
×
171
                    forType: UInt64.self)
×
172
        }
4✔
173
        defer { currentIndex += 1 }
4✔
174

4✔
175
        if alignmentContext == .message {
4✔
176
            pad(to: signature[currentIndex].alignment)
3✔
177
        }
3✔
178
        _data.append(contentsOf: try marshal(value))
4✔
179
    }
4✔
180

181
    /// Marshal `Int16` value into bytes.
182
    private func marshal(_ value: Int16) throws -> [UInt8] {
9✔
183
        var data: [UInt8] = []
9✔
184
        data.append(contentsOf: try byteSize(value))
9✔
185
        return data
9✔
186
    }
9✔
187

188
    /// Encode `value` as a `Int16`.
189
    public mutating func serialize(_ value: Int16) throws {
3✔
190
        guard signature[currentIndex] == .int16 else {
3✔
191
            throw
×
192
                SerializerError
×
193
                .signatureElementMismatch(
×
194
                    gotElement: signature[currentIndex],
×
195
                    forType: Int16.self)
×
196
        }
3✔
197
        defer { currentIndex += 1 }
3✔
198

3✔
199
        if alignmentContext == .message {
3✔
200
            pad(to: signature[currentIndex].alignment)
3✔
201
        }
3✔
202
        _data.append(contentsOf: try marshal(value))
3✔
203
    }
3✔
204

205
    /// Marshal `Int32` value into bytes.
206
    private func marshal(_ value: Int32) throws -> [UInt8] {
1,048✔
207
        var data: [UInt8] = []
1,048✔
208
        data.append(contentsOf: try byteSize(value))
1,048✔
209
        return data
1,048✔
210
    }
1,048✔
211

212
    /// Encode `value` as a `Int32`.
213
    public mutating func serialize(_ value: Int32) throws {
20✔
214
        guard signature[currentIndex] == .int32 else {
20✔
215
            throw
×
216
                SerializerError
×
217
                .signatureElementMismatch(
×
218
                    gotElement: signature[currentIndex],
×
219
                    forType: Int32.self)
×
220
        }
20✔
221
        defer { currentIndex += 1 }
20✔
222

20✔
223
        if alignmentContext == .message {
20✔
224
            pad(to: signature[currentIndex].alignment)
17✔
225
        }
17✔
226
        _data.append(contentsOf: try marshal(value))
20✔
227
    }
20✔
228

229
    /// Marshal `Int64` value into bytes.
230
    private func marshal(_ value: Int64) throws -> [UInt8] {
8✔
231
        var data: [UInt8] = []
8✔
232
        data.append(contentsOf: try byteSize(value))
8✔
233
        return data
8✔
234
    }
8✔
235

236
    /// Encode `value` as a `Int64`.
237
    public mutating func serialize(_ value: Int64) throws {
3✔
238
        guard signature[currentIndex] == .int64 else {
3✔
239
            throw
×
240
                SerializerError
×
241
                .signatureElementMismatch(
×
242
                    gotElement: signature[currentIndex],
×
243
                    forType: Int64.self)
×
244
        }
3✔
245
        defer { currentIndex += 1 }
3✔
246

3✔
247
        if alignmentContext == .message {
3✔
248
            pad(to: signature[currentIndex].alignment)
3✔
249
        }
3✔
250
        _data.append(contentsOf: try marshal(value))
3✔
251
    }
3✔
252

253
    /// Marshal `Double` value into bytes.
254
    private func marshal(_ value: Double) throws -> [UInt8] {
42✔
255
        var data: [UInt8] = []
42✔
256
        // Serialize Double directly with proper endianness
42✔
257
        let bitPattern = value.bitPattern
42✔
258
        if endianness == .littleEndian {
42✔
259
            data.append(contentsOf: withUnsafeBytes(of: bitPattern.littleEndian) { Array($0) })
42✔
260
        } else {
42✔
261
            data.append(contentsOf: withUnsafeBytes(of: bitPattern.bigEndian) { Array($0) })
×
262
        }
×
263
        return data
42✔
264
    }
42✔
265

266
    /// Encode `value` as a `Double`.
267
    public mutating func serialize(_ value: Double) throws {
3✔
268
        guard signature[currentIndex] == .double else {
3✔
269
            throw
×
270
                SerializerError
×
271
                .signatureElementMismatch(
×
272
                    gotElement: signature[currentIndex],
×
273
                    forType: Double.self)
×
274
        }
3✔
275
        defer { currentIndex += 1 }
3✔
276

3✔
277
        if alignmentContext == .message {
3✔
278
            pad(to: signature[currentIndex].alignment)
3✔
279
        }
3✔
280
        _data.append(contentsOf: try marshal(value))
3✔
281
    }
3✔
282

283
    /// Marshal `String` value into bytes.
284
    private func marshal(_ value: String) throws -> [UInt8] {
7,416✔
285
        var data: [UInt8] = []
7,416✔
286
        let length = value.utf8.count
7,416✔
287
        data.append(contentsOf: try byteSize(UInt32(length)))
7,416✔
288
        data.append(contentsOf: value.utf8)
7,416✔
289
        data.append(0x00)
7,416✔
290
        return data
7,416✔
291
    }
7,416✔
292

293
    /// Encode `value` as a `String`.
294
    public mutating func serialize(_ value: String) throws {
49✔
295
        guard signature[currentIndex] == .string else {
49✔
296
            throw
×
297
                SerializerError
×
298
                .signatureElementMismatch(
×
299
                    gotElement: signature[currentIndex],
×
300
                    forType: String.self)
×
301
        }
49✔
302
        defer { currentIndex += 1 }
49✔
303

49✔
304
        if alignmentContext == .message {
49✔
305
            pad(to: signature[currentIndex].alignment)
44✔
306
        }
44✔
307
        _data.append(contentsOf: try marshal(value))
49✔
308
    }
49✔
309

310
    /// Marshal `ObjectPath` value into bytes.
311
    private func marshal(_ value: ObjectPath) throws -> [UInt8] {
16✔
312
        return try marshal(value.fullPath)
16✔
313
    }
16✔
314

315
    /// Encode `value` as a `ObjectPath`.
316
    public mutating func serialize(_ value: ObjectPath) throws {
3✔
317
        guard signature[currentIndex] == .objectPath else {
3✔
318
            throw
×
319
                SerializerError
×
320
                .signatureElementMismatch(
×
321
                    gotElement: signature[currentIndex],
×
322
                    forType: ObjectPath.self)
×
323
        }
3✔
324
        defer { currentIndex += 1 }
3✔
325

3✔
326
        if alignmentContext == .message {
3✔
327
            pad(to: signature[currentIndex].alignment)
2✔
328
        }
2✔
329
        _data.append(contentsOf: try marshal(value))
3✔
330
    }
3✔
331

332
    /// Marshal `Signature` value into bytes.
333
    private func marshal(_ value: Signature) throws -> [UInt8] {
15✔
334
        var data: [UInt8] = []
15✔
335
        let length = value.rawValue.utf8.count
15✔
336
        data.append(contentsOf: try byteSize(UInt8(length)))
15✔
337
        data.append(contentsOf: value.rawValue.utf8)
15✔
338
        data.append(0x00)
15✔
339
        return data
15✔
340
    }
15✔
341

342
    /// Encode `value` as a `Signature`.
343
    public mutating func serialize(_ value: Signature) throws {
2✔
344
        guard signature[currentIndex] == .signature else {
2✔
345
            throw
×
346
                SerializerError
×
347
                .signatureElementMismatch(
×
348
                    gotElement: signature[currentIndex],
×
349
                    forType: Signature.self)
×
350
        }
2✔
351
        defer { currentIndex += 1 }
2✔
352

2✔
353
        if alignmentContext == .message {
2✔
354
            pad(to: signature[currentIndex].alignment)
2✔
355
        }
2✔
356
        _data.append(contentsOf: try marshal(value))
2✔
357
    }
2✔
358

359
    /// Marshal `DBusVariant` value into bytes.
360
    private func marshal(_ value: Variant) throws -> [UInt8] {
148✔
361
        var data: [UInt8] = []
148✔
362

148✔
363
        // D-Bus variant format: signature (1 byte length + string + null) + aligned value
148✔
364
        let sigBytes = value.signature.rawValue.utf8
148✔
365
        data.append(UInt8(sigBytes.count))
148✔
366
        data.append(contentsOf: sigBytes)
148✔
367
        data.append(0x00)  // null terminator
148✔
368

148✔
369
        // Apply alignment for the value based on its signature
148✔
370
        let alignment = value.signature.element?.alignment ?? 1
148✔
371
        let currentSize = data.count
148✔
372
        let paddingNeeded = (alignment - (currentSize % alignment)) % alignment
148✔
373
        data.append(contentsOf: Array(repeating: 0, count: paddingNeeded))
148✔
374

148✔
375
        // Now marshal the actual value based on its type
148✔
376
        switch value.value {
148✔
377
        case .byte(let v):
148✔
378
            data.append(contentsOf: try marshal(v))
4✔
379
        case .bool(let v):
148✔
380
            data.append(contentsOf: try marshal(v))
4✔
381
        case .int16(let v):
148✔
382
            data.append(contentsOf: try marshal(v))
4✔
383
        case .uint16(let v):
148✔
384
            data.append(contentsOf: try marshal(v))
3✔
385
        case .int32(let v):
148✔
386
            data.append(contentsOf: try marshal(v))
5✔
387
        case .uint32(let v):
148✔
388
            data.append(contentsOf: try marshal(v))
4✔
389
        case .int64(let v):
148✔
390
            data.append(contentsOf: try marshal(v))
4✔
391
        case .uint64(let v):
148✔
392
            data.append(contentsOf: try marshal(v))
3✔
393
        case .double(let v):
148✔
394
            data.append(contentsOf: try marshal(v))
19✔
395
        case .string(let v):
148✔
396
            data.append(contentsOf: try marshal(v))
23✔
397
        case .objectPath(let v):
148✔
398
            data.append(contentsOf: try marshal(v))
8✔
399
        case .signature(let v):
148✔
400
            data.append(contentsOf: try marshal(v))
11✔
401
        case .array(let elements):
148✔
402
            data.append(contentsOf: try marshalVariantArray(elements, signature: value.signature))
21✔
403
        case .dictionary(let dict):
148✔
404
            data.append(contentsOf: try marshalVariantDictionary(dict, signature: value.signature))
15✔
405
        case .`struct`(let elements):
148✔
406
            data.append(contentsOf: try marshalVariantStruct(elements, signature: value.signature))
20✔
407
        }
148✔
408

148✔
409
        return data
148✔
410
    }
148✔
411

412
    /// Encode `value` as a `DBusVariant`.
413
    public mutating func serialize(_ value: Variant) throws {
144✔
414
        guard signature[currentIndex] == .variant else {
144✔
415
            throw
×
416
                SerializerError
×
417
                .signatureElementMismatch(
×
418
                    gotElement: signature[currentIndex],
×
419
                    forType: Variant.self)
×
420
        }
144✔
421
        defer { currentIndex += 1 }
144✔
422

144✔
423
        if alignmentContext == .message {
144✔
424
            pad(to: signature[currentIndex].alignment)
144✔
425
        }
144✔
426
        _data.append(contentsOf: try marshal(value))
144✔
427
    }
144✔
428

429
    /// Marshal `T` value into bytes.
430
    private func marshal<T>(_ value: T) throws -> [UInt8] {
6,052✔
431
        switch value {
6,052✔
432
        case let v as Bool: return try marshal(v)
6,052✔
433
        case let v as UInt8: return try marshal(v)
6,052✔
434
        case let v as UInt16: return try marshal(v)
6,052✔
435
        case let v as UInt32: return try marshal(v)
6,052✔
436
        case let v as UInt64: return try marshal(v)
6,052✔
437
        case let v as Int16: return try marshal(v)
6,052✔
438
        case let v as Int32: return try marshal(v)
6,052✔
439
        case let v as Int64: return try marshal(v)
6,052✔
440
        case let v as Double: return try marshal(v)
6,052✔
441
        case let v as String: return try marshal(v)
6,052✔
442
        case let v as ObjectPath: return try marshal(v)
6,052✔
443
        case let v as Signature: return try marshal(v)
6,052✔
444
        case let v as Variant: return try marshal(v)
6,052✔
445
        default:
6,052✔
446
            throw SerializerError.cannotMarshalType(type: T.self)
×
447
        }
6,052✔
448
    }
6,052✔
449

450
    /// Encode `value` as an array of type `T`.
451
    mutating func serialize<T>(_ value: [T]) throws {
25✔
452
        guard case .array(let arrayElement) = signature[currentIndex],
25✔
453
            arrayElement == SignatureElement(T.self)
25✔
454
        else {
25✔
455
            throw
×
456
                SerializerError
×
457
                .signatureElementMismatch(gotElement: signature[currentIndex], forType: [T].self)
×
458
        }
25✔
459
        defer { currentIndex += 1 }
25✔
460

25✔
461
        if alignmentContext == .message {
25✔
462
            pad(to: signature[currentIndex].alignment)
24✔
463
        }
24✔
464

25✔
465
        var array: [UInt8] = []
25✔
466
        for (index, element) in value.enumerated() {
6,032✔
467
            array.append(contentsOf: try marshal(element))
6,032✔
468

6,032✔
469
            // Apply padding for string and ObjectPath elements to maintain 4-byte alignment
6,032✔
470
            // (except for the last element)
6,032✔
471
            if index < value.count - 1 {
6,032✔
472
                switch signature[currentIndex] {
6,010✔
473
                case .array(.string), .array(.objectPath):
6,010✔
474
                    let padding = (4 - (array.count % 4)) % 4
5,988✔
475
                    array.append(contentsOf: Array(repeating: 0, count: padding))
5,988✔
476
                default:
6,010✔
477
                    break
9✔
478
                }
6,010✔
479
            }
6,019✔
480
        }
6,019✔
481

12✔
482
        let length = array.count
12✔
483

12✔
484
        _data.append(contentsOf: try byteSize(UInt32(length)))
12✔
485
        _data.append(contentsOf: array)
12✔
486
    }
12✔
487

488
    /// Encode `value` as a dictionary of (`K`, `V`).
489
    mutating func serialize<K: Hashable, V>(_ value: [K: V]) throws {
7✔
490
        guard case .dictionary(let keyElement, let valueElement) = signature[currentIndex],
7✔
491
            keyElement == SignatureElement(K.self), valueElement == SignatureElement(V.self)
7✔
492
        else {
7✔
493
            throw
×
494
                SerializerError
×
495
                .signatureElementMismatch(
×
496
                    gotElement: signature[currentIndex],
×
497
                    forType: Dictionary<K, V>.self)
×
498
        }
7✔
499
        defer { currentIndex += 1 }
7✔
500

7✔
501
        if alignmentContext == .message {
7✔
502
            pad(to: signature[currentIndex].alignment)
7✔
503
        }
7✔
504

7✔
505
        // Build dictionary entries data first to calculate the correct length
7✔
506
        var dictData: [UInt8] = []
7✔
507
        for (k, v) in value {
14✔
508
            // Dictionary entries are aligned to 8-byte boundaries
14✔
509
            let padding = (8 - (dictData.count % 8)) % 8
14✔
510
            dictData.append(contentsOf: Array(repeating: 0, count: padding))
14✔
511

14✔
512
            dictData.append(contentsOf: try marshal(k))
14✔
513
            dictData.append(contentsOf: try marshal(v))
14✔
514
        }
14✔
515

7✔
516
        // Write the length as UInt32 (following the array format for dictionaries)
7✔
517
        _data.append(contentsOf: try byteSize(UInt32(dictData.count)))
7✔
518

7✔
519
        // Apply 8-byte alignment after length field for dictionary entries (as per D-Bus spec)
7✔
520
        let paddingNeeded = (8 - (_data.count % 8)) % 8
7✔
521
        _data.append(contentsOf: Array(repeating: 0, count: paddingNeeded))
7✔
522

7✔
523
        _data.append(contentsOf: dictData)
7✔
524
    }
7✔
525

526
    /// Encode a value using another serializer. If the signature is not a STRUCT container,
527
    /// an error is thrown. The "subserializer" is initialized with the STRUCT container
528
    /// elements. For example, if the serializer's signature is `(ux)`, the nested serializer's
529
    /// signature will be `ux`. The caller may then serialize the contents of the struct
530
    /// accordingly, using the provided serializer.
531
    mutating func serialize(_ f: (inout Serializer) throws -> Void) throws {
5✔
532
        guard case .struct(let structElements) = signature[currentIndex] else {
5✔
533
            throw
×
534
                SerializerError
×
535
                .cannotMarshalElement(gotElement: signature[currentIndex])
×
536
        }
5✔
537
        defer { currentIndex += 1 }
5✔
538

5✔
539
        if alignmentContext == .message {
5✔
540
            pad(to: signature[currentIndex].alignment)
5✔
541
        }
5✔
542

5✔
543
        var serializer = Serializer(
5✔
544
            signature: Signature(elements: structElements),
5✔
545
            alignmentContext: .structContent,
5✔
546
            endianness: endianness)
5✔
547
        try f(&serializer)
5✔
548
        _data.append(contentsOf: serializer.data!)
5✔
549
    }
5✔
550

551
    /// Separate the given fixed-width integer value into bytes by shifting the value one byte
552
    /// at a time and store the least significant bits each time.
553
    private func byteSize<T: FixedWidthInteger>(_ value: T) throws -> [UInt8] {
8,601✔
554
        if endianness == .littleEndian {
8,601✔
555
            return withUnsafeBytes(of: value.littleEndian) { Array($0) }
8,605✔
556
        } else {
8,601✔
557
            return withUnsafeBytes(of: value.bigEndian) { Array($0) }
×
558
        }
×
559
    }
8,601✔
560

561
    /// Appends a number of NULL bytes to `_data` to align the buffer to a given alignment.
562
    ///
563
    /// Parameters:
564
    /// - alignment: Boundary within the sequence to align to a factor of
565
    ///
566
    /// Returns: A number of elements to advance to align
567
    private mutating func pad(to alignment: Int) {
274✔
568
        if _data.count % alignment != 0 {
274✔
569
            let newIndex = (_data.count + alignment - 1) & ~(alignment - 1)
4✔
570
            let padding = newIndex - _data.count
4✔
571
            for _ in 0..<padding {
12✔
572
                _data.append(0x00)
12✔
573
            }
12✔
574
        }
4✔
575
    }
274✔
576

577
    /// Marshal a variant array into D-Bus format
578
    private func marshalVariantArray(_ elements: [VariantValue], signature: Signature) throws
579
        -> [UInt8]
580
    {
25✔
581
        guard let signatureElement = signature.element,
25✔
582
            case .array(let arrayElement) = signatureElement
25✔
583
        else {
25✔
584
            throw SerializerError.cannotMarshalType(type: [VariantValue].self)
×
585
        }
25✔
586

25✔
587
        var arrayData: [UInt8] = []
25✔
588

25✔
589
        // Marshal each element
25✔
590
        for (index, element) in elements.enumerated() {
2,044✔
591
            // Marshal the element using its signature
2,044✔
592
            let elementSignature = Signature(elements: [arrayElement])
2,044✔
593
            let elementData = try marshalVariantValue(element, signature: elementSignature)
2,044✔
594
            arrayData.append(contentsOf: elementData)
2,044✔
595

2,044✔
596
            // Add padding for string elements to maintain 4-byte alignment (except for the last element)
2,044✔
597
            if arrayElement == .string && index < elements.count - 1 {
2,044✔
598
                let padding = (4 - (arrayData.count % 4)) % 4
1,006✔
599
                arrayData.append(contentsOf: Array(repeating: 0, count: padding))
1,006✔
600
            }
1,006✔
601
        }
2,044✔
602

25✔
603
        // Create the final array data with length prefix
25✔
604
        var result: [UInt8] = []
25✔
605

25✔
606
        // Array length (4 bytes)
25✔
607
        let arrayLength = UInt32(arrayData.count)
25✔
608
        let lengthBytes: [UInt8]
25✔
609
        if endianness == .littleEndian {
25✔
610
            lengthBytes = withUnsafeBytes(of: arrayLength.littleEndian) { Array($0) }
25✔
611
        } else {
25✔
612
            lengthBytes = withUnsafeBytes(of: arrayLength.bigEndian) { Array($0) }
×
613
        }
×
614
        result.append(contentsOf: lengthBytes)
25✔
615

25✔
616
        // Add alignment padding for the first element based on its alignment requirements
25✔
617
        // According to D-Bus spec, arrays must pad to the element's alignment boundary
25✔
618
        let alignment = arrayElement.alignment
25✔
619
        let paddingNeeded = (alignment - (result.count % alignment)) % alignment
25✔
620
        result.append(contentsOf: Array(repeating: 0, count: paddingNeeded))
25✔
621

25✔
622
        // Array data
25✔
623
        result.append(contentsOf: arrayData)
25✔
624

25✔
625
        return result
25✔
626
    }
25✔
627

628
    /// Marshal a variant dictionary into D-Bus format
629
    private func marshalVariantDictionary(_ dict: [String: VariantValue], signature: Signature)
630
        throws -> [UInt8]
631
    {
18✔
632
        guard let signatureElement = signature.element,
18✔
633
            case .dictionary(let keyElement, let valueElement) = signatureElement
18✔
634
        else {
18✔
635
            throw SerializerError.cannotMarshalType(type: [String: VariantValue].self)
×
636
        }
18✔
637

18✔
638
        var dictData: [UInt8] = []
18✔
639

18✔
640
        // Marshal each key-value pair
18✔
641
        for (key, value) in dict {
140✔
642
            // Apply 8-byte alignment for each dictionary entry
140✔
643
            let entryPadding = (8 - (dictData.count % 8)) % 8
140✔
644
            dictData.append(contentsOf: Array(repeating: 0, count: entryPadding))
140✔
645

140✔
646
            // Marshal key
140✔
647
            let keySignature = Signature(elements: [keyElement])
140✔
648
            let keyData = try marshalVariantValue(.string(key), signature: keySignature)
140✔
649
            dictData.append(contentsOf: keyData)
140✔
650

140✔
651
            // Marshal value
140✔
652
            let valueSignature = Signature(elements: [valueElement])
140✔
653
            let valueData = try marshalVariantValue(value, signature: valueSignature)
140✔
654
            dictData.append(contentsOf: valueData)
140✔
655
        }
140✔
656

18✔
657
        // Create the final dictionary data with length prefix
18✔
658
        var result: [UInt8] = []
18✔
659

18✔
660
        // Dictionary length (4 bytes)
18✔
661
        let dictLength = UInt32(dictData.count)
18✔
662
        let lengthBytes: [UInt8]
18✔
663
        if endianness == .littleEndian {
18✔
664
            lengthBytes = withUnsafeBytes(of: dictLength.littleEndian) { Array($0) }
18✔
665
        } else {
18✔
666
            lengthBytes = withUnsafeBytes(of: dictLength.bigEndian) { Array($0) }
×
667
        }
×
668
        result.append(contentsOf: lengthBytes)
18✔
669

18✔
670
        // Apply 8-byte alignment after length field (as per D-Bus spec)
18✔
671
        let paddingNeeded = (8 - (result.count % 8)) % 8
18✔
672
        result.append(contentsOf: Array(repeating: 0, count: paddingNeeded))
18✔
673

18✔
674
        // Dictionary data
18✔
675
        result.append(contentsOf: dictData)
18✔
676

18✔
677
        return result
18✔
678
    }
18✔
679

680
    /// Marshal a variant struct into D-Bus format
681
    private func marshalVariantStruct(_ elements: [VariantValue], signature: Signature) throws
682
        -> [UInt8]
683
    {
26✔
684
        guard let signatureElement = signature.element,
26✔
685
            case .struct(let structElements) = signatureElement
26✔
686
        else {
26✔
687
            throw SerializerError.cannotMarshalType(type: [VariantValue].self)
×
688
        }
26✔
689

26✔
690
        guard elements.count == structElements.count else {
26✔
691
            throw SerializerError.cannotMarshalType(type: [VariantValue].self)
×
692
        }
26✔
693

26✔
694
        var structData: [UInt8] = []
26✔
695

26✔
696
        // Apply 8-byte alignment for struct
26✔
697
        let structPadding = (8 - (structData.count % 8)) % 8
26✔
698
        structData.append(contentsOf: Array(repeating: 0, count: structPadding))
26✔
699

26✔
700
        // Marshal each element
26✔
701
        for (index, element) in elements.enumerated() {
57✔
702
            let elementSignatureElement = structElements[index]
57✔
703

57✔
704
            // Apply alignment for each element
57✔
705
            let alignment = elementSignatureElement.alignment
57✔
706
            let paddingNeeded = (alignment - (structData.count % alignment)) % alignment
57✔
707
            structData.append(contentsOf: Array(repeating: 0, count: paddingNeeded))
57✔
708

57✔
709
            // Marshal the element using its signature
57✔
710
            let elementSignature = Signature(elements: [elementSignatureElement])
57✔
711
            let elementData = try marshalVariantValue(element, signature: elementSignature)
57✔
712
            structData.append(contentsOf: elementData)
57✔
713
        }
57✔
714

26✔
715
        return structData
26✔
716
    }
26✔
717

718
    /// Marshal a variant value based on its type and signature
719
    private func marshalVariantValue(_ value: VariantValue, signature: Signature) throws
720
        -> [UInt8]
721
    {
2,376✔
722
        switch value {
2,376✔
723
        case .byte(let v):
2,376✔
724
            return try marshal(v)
9✔
725
        case .bool(let v):
2,376✔
726
            return try marshal(v)
10✔
727
        case .int16(let v):
2,376✔
728
            return try marshal(v)
1✔
729
        case .uint16(let v):
2,376✔
730
            return try marshal(v)
×
731
        case .int32(let v):
2,376✔
732
            return try marshal(v)
1,017✔
733
        case .uint32(let v):
2,376✔
734
            return try marshal(v)
4✔
735
        case .int64(let v):
2,376✔
736
            return try marshal(v)
×
737
        case .uint64(let v):
2,376✔
738
            return try marshal(v)
×
739
        case .double(let v):
2,376✔
740
            return try marshal(v)
14✔
741
        case .string(let v):
2,376✔
742
            return try marshal(v)
1,306✔
743
        case .objectPath(let v):
2,376✔
744
            return try marshal(v)
2✔
745
        case .signature(let v):
2,376✔
746
            return try marshal(v)
2✔
747
        case .array(let elements):
2,376✔
748
            return try marshalVariantArray(elements, signature: signature)
4✔
749
        case .dictionary(let dict):
2,376✔
750
            return try marshalVariantDictionary(dict, signature: signature)
3✔
751
        case .`struct`(let elements):
2,376✔
752
            return try marshalVariantStruct(elements, signature: signature)
6✔
753
        }
2,376✔
754
    }
2,376✔
755
}
756

757
enum SerializerError: Error {
758
    case signatureElementMismatch(gotElement: SignatureElement, forType: Any.Type)
759
    case invalidValue(forType: Any.Type)
760
    case cannotMarshalType(type: Any.Type)
761
    case cannotMarshalElement(gotElement: SignatureElement)
762
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc