Skip to content

Commit 9476d5d

Browse files
authored
Merge pull request #51 from orchetect/tcp-slip-hotfix
TCP SLIP Decoding Bug HotFix
2 parents 8c44987 + e33b984 commit 9476d5d

File tree

2 files changed

+57
-24
lines changed

2 files changed

+57
-24
lines changed

Sources/OSCKit/TCP/Framing/SLIP Coding.swift

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ extension Data {
5252
///
5353
/// This can accommodate one or more packets in the same data stream. Each packet is
5454
/// returned as an element in the array.
55-
func slipDecoded() throws -> [Data] {
55+
func slipDecoded() throws(OSCTCPSLIPDecodingError) -> [Data] {
5656
var packets: [Data] = []
5757

5858
var currentPacketData = Data()
@@ -62,7 +62,9 @@ extension Data {
6262
switch self[index] {
6363
case SLIPByte.end.rawValue:
6464
// END should never come after an escape byte
65-
guard !isEscaped else { throw OSCTCPSLIPDecodingError.missingEscapedCharacter}
65+
guard !isEscaped else {
66+
throw .missingEscapedCharacter
67+
}
6668

6769
// consider the END byte the end of the current packet
6870
if !currentPacketData.isEmpty {
@@ -74,34 +76,44 @@ extension Data {
7476

7577
case SLIPByte.esc.rawValue:
7678
// we should never get more than one consecutive ESC byte
77-
guard !isEscaped else { throw OSCTCPSLIPDecodingError.doubleEscapeBytes}
79+
guard !isEscaped else {
80+
throw .doubleEscapeBytes
81+
}
7882

7983
isEscaped = true
8084

8185
case SLIPByte.escEnd.rawValue:
82-
// must follow an ESC byte
83-
guard isEscaped else { throw OSCTCPSLIPDecodingError.missingEscapeByte}
84-
isEscaped = false // reset ESC
85-
86-
currentPacketData.append(SLIPByte.end.rawValue)
86+
// if following an ESC byte, translate it
87+
if isEscaped {
88+
isEscaped = false // reset ESC
89+
currentPacketData.append(SLIPByte.end.rawValue)
90+
} else {
91+
currentPacketData.append(SLIPByte.escEnd.rawValue)
92+
}
8793

8894
case SLIPByte.escEsc.rawValue:
89-
// must follow an ESC byte
90-
guard isEscaped else { throw OSCTCPSLIPDecodingError.missingEscapeByte}
91-
isEscaped = false // reset ESC
92-
93-
currentPacketData.append(SLIPByte.esc.rawValue)
95+
// if following an ESC byte, translate it
96+
if isEscaped {
97+
isEscaped = false // reset ESC
98+
currentPacketData.append(SLIPByte.esc.rawValue)
99+
} else {
100+
currentPacketData.append(SLIPByte.escEsc.rawValue)
101+
}
94102

95103
default:
96104
// the only two bytes that should follow an ESC byte are ESC_END and ESC_ESC
97-
guard !isEscaped else { throw OSCTCPSLIPDecodingError.missingEscapedCharacter}
105+
guard !isEscaped else {
106+
throw .missingEscapedCharacter
107+
}
98108

99109
currentPacketData.append(self[index])
100110
}
101111
}
102112

103113
// failsafe: ensure we are not ending while escaped (check if final byte was ESC)
104-
guard !isEscaped else { throw OSCTCPSLIPDecodingError.missingEscapedCharacter}
114+
guard !isEscaped else {
115+
throw .missingEscapedCharacter
116+
}
105117

106118
// add final packet if needed
107119
if !currentPacketData.isEmpty {

Tests/OSCKitTests/TCP/SLIP Coding Tests.swift

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,26 @@ import Testing
123123
}
124124
}
125125

126-
/// Test for error: encountering an escaped character without first receiving an ESC byte.
126+
/// Encountering an escaped character without first receiving an ESC byte
127+
/// should treat the byte as-is.
127128
@Test
128129
func dataSlipDecoded_MissingEscapeByte() throws {
129-
#expect(throws: OSCTCPSLIPDecodingError.missingEscapeByte) {
130+
#expect(
130131
try Data([END, 0x01, ESC_ESC, 0x02, END]).slipDecoded()
131-
}
132-
#expect(throws: OSCTCPSLIPDecodingError.missingEscapeByte) {
132+
== [Data([0x01, ESC_ESC, 0x02])]
133+
)
134+
#expect(
133135
try Data([END, 0x01, ESC_END, 0x02, END]).slipDecoded()
134-
}
135-
#expect(throws: OSCTCPSLIPDecodingError.missingEscapeByte) {
136+
== [Data([0x01, ESC_END, 0x02])]
137+
)
138+
#expect(
136139
try Data([END, ESC_ESC, END]).slipDecoded()
137-
}
138-
#expect(throws: OSCTCPSLIPDecodingError.missingEscapeByte) {
140+
== [Data([ESC_ESC])]
141+
)
142+
#expect(
139143
try Data([END, ESC_END, END]).slipDecoded()
140-
}
144+
== [Data([ESC_END])]
145+
)
141146
}
142147

143148
/// Test for error: missing valid escaped character after receiving ESC byte.
@@ -170,4 +175,20 @@ import Testing
170175
let decodedData = try encodedData.slipDecoded()
171176
#expect(decodedData == [oscRawData])
172177
}
178+
179+
/// Test encoding all possible byte values.
180+
@Test
181+
func allByteValuesSlipEncodeDecode() throws {
182+
for value in UInt8(0) ... UInt8(255) {
183+
let valueByte = Data([value])
184+
let byteDescription = "Byte \(value.hexString(prefix: true))"
185+
let encoded = valueByte.slipEncoded()
186+
do {
187+
let decoded = try encoded.slipDecoded()
188+
#expect(decoded == [valueByte], "\(byteDescription)")
189+
} catch {
190+
Issue.record("\(byteDescription) error: \(error.localizedDescription)")
191+
}
192+
}
193+
}
173194
}

0 commit comments

Comments
 (0)