This repository was archived by the owner on Jun 30, 2026. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathCOBS.swift
More file actions
96 lines (82 loc) · 2.53 KB
/
Copy pathCOBS.swift
File metadata and controls
96 lines (82 loc) · 2.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/// COBS (Consistent Overhead Byte Stuffing) framing.
///
/// r[impl transport.bytestream.cobs] - Messages are COBS-encoded with 0x00 delimiter.
/// r[impl transport.message.binary] - All messages are binary (not text).
/// r[impl transport.message.one-to-one] - Each frame contains exactly one roam message.
///
/// COBS encodes data so that it contains no zero bytes, allowing
/// zero to be used as a frame delimiter.
/// Encode data using COBS.
///
/// The output will contain no zero bytes. A zero byte should be
/// appended as a frame delimiter after sending.
public func cobsEncode(_ data: [UInt8]) -> [UInt8] {
// Empty input produces empty output
guard !data.isEmpty else {
return []
}
var output: [UInt8] = []
output.reserveCapacity(data.count + data.count / 254 + 1)
var codeIndex = 0
var code: UInt8 = 1
output.append(0) // Placeholder for first code byte
for byte in data {
if byte == 0 {
output[codeIndex] = code
code = 1
codeIndex = output.count
output.append(0) // Placeholder for next code byte
} else {
output.append(byte)
code += 1
if code == 0xFF {
output[codeIndex] = code
code = 1
codeIndex = output.count
output.append(0) // Placeholder for next code byte
}
}
}
output[codeIndex] = code
return output
}
/// Decode COBS-encoded data.
///
/// Input should NOT include the trailing zero delimiter.
public func cobsDecode(_ encoded: [UInt8]) throws -> [UInt8] {
guard !encoded.isEmpty else {
return []
}
var output: [UInt8] = []
output.reserveCapacity(encoded.count)
var i = 0
while i < encoded.count {
let code = encoded[i]
if code == 0 {
throw COBSError.unexpectedZero
}
i += 1
let copyCount = Int(code) - 1
if i + copyCount > encoded.count {
throw COBSError.truncated
}
for j in 0..<copyCount {
let byte = encoded[i + j]
if byte == 0 {
throw COBSError.unexpectedZero
}
output.append(byte)
}
i += copyCount
// If code < 0xFF, we implicitly have a zero (unless at end)
if code < 0xFF && i < encoded.count {
output.append(0)
}
}
return output
}
/// Errors that can occur during COBS decoding.
public enum COBSError: Error {
case unexpectedZero
case truncated
}