Skip to content

Commit bcc4031

Browse files
committed
Add decoding of arrays
1 parent a87ce04 commit bcc4031

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

Sources/NIORedis/Coders/RedisDataDecoder.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ extension RedisDataDecoder {
5959
return .parsed
6060
case .dollar:
6161
return try _parseBulkString(at: &position, from: buffer)
62+
case .asterisk:
63+
return try _parseArray(at: &position, from: buffer)
6264
default: return .notYetParsed
6365
}
6466
}
@@ -139,6 +141,31 @@ extension RedisDataDecoder {
139141

140142
return .parsed // bulkString(Data(bytes[ ..<(size - 1) ]))
141143
}
144+
145+
/// See https://redis.io/topics/protocol#resp-arrays
146+
func _parseArray(at position: inout Int, from buffer: ByteBuffer) throws -> _RedisDataDecodingState {
147+
guard let arraySize = try _parseInteger(at: &position, from: buffer) else { return .notYetParsed }
148+
#warning("TODO: return null array")
149+
guard arraySize > -1 else { return .parsed }
150+
#warning("TODO: return empty array")
151+
guard arraySize > 0 else { return .parsed }
152+
153+
var array = [_RedisDataDecodingState](repeating: .notYetParsed, count: arraySize)
154+
for index in 0..<arraySize {
155+
guard buffer.readableBytes - position > 0 else { return .notYetParsed }
156+
157+
let parseResult = try _parse(at: &position, from: buffer)
158+
switch parseResult {
159+
case .parsed:
160+
array[index] = parseResult
161+
default:
162+
return .notYetParsed
163+
}
164+
}
165+
166+
#warning("TODO: Mapping to data and return the array of values")
167+
return .parsed
168+
}
142169
}
143170

144171
private extension ByteBuffer {

Tests/NIORedisTests/RedisDataDecoder+ParsingTests.swift

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ final class RedisDataDecoderParsingTests: XCTestCase {
4141
])
4242
}
4343

44+
func testParsing_with_arrays() throws {
45+
try parseTest_singleValue(input: "*1\r\n+!\r\n")
46+
try parseTest_singleValue(input: "*2\r\n*1\r\n:1\r\n:3\r\n")
47+
try parseTest_singleValue(input: "*0\r\n".convertedToData())
48+
try parseTest_singleValue(input: "*-1\r\n".convertedToData())
49+
}
50+
51+
func testParsing_with_arrays_recursively() throws {
52+
try parseTest_recursive(withChunks: ["*2\r", "\n+a\r\n+a\r\n*", "0\r\n"])
53+
try parseTest_recursive(withChunks: ["*-1\r".convertedToData(), "\n".convertedToData()])
54+
}
55+
4456
/// See parse_Test_singleValue(input:) String
4557
private func parseTest_singleValue(input: String) throws {
4658
try parseTest_singleValue(input: input.convertedToData())
@@ -208,7 +220,7 @@ extension RedisDataDecoderParsingTests {
208220
}
209221

210222
func testParsing_bulkString_handlesLargeSizes() throws {
211-
let bytes = [UInt8].init(repeating: .dollar, count: 10_000_000)
223+
let bytes = [UInt8].init(repeating: .dollar, count: 1_000_000)
212224
let data = "$\(bytes.count)\r\n".convertedToData() + Data(bytes: bytes) + "\r\n".convertedToData()
213225
XCTAssertEqual(try parseTestBulkString(data), .parsed)
214226
}
@@ -227,6 +239,55 @@ extension RedisDataDecoderParsingTests {
227239
}
228240
}
229241

242+
// MARK: Array Parsing
243+
244+
extension RedisDataDecoderParsingTests {
245+
func testParsing_array_whenNull_returnsNil() {
246+
XCTAssertEqual(try parseTestArray("*-1\r\n"), .parsed)
247+
}
248+
249+
func testParsing_array_whenEmpty_returnsEmpty() {
250+
XCTAssertEqual(try parseTestArray("*0\r\n"), .parsed)
251+
}
252+
253+
func testParsing_array_handlesLargeSizes() {
254+
let range = 0..<1000
255+
var data = "*\(range.endIndex)\r\n".convertedToData()
256+
range.forEach { _ in
257+
data += "$5\r\n".convertedToData()
258+
data += Data(bytes: [0xaa, 0xbb, 0xcc, 0xab, 0xff])
259+
data += "\r\n".convertedToData()
260+
}
261+
262+
XCTAssertEqual(try parseTestArray(data), .parsed)
263+
}
264+
265+
func testParsing_array_handlesMixedTypes() {
266+
XCTAssertEqual(try parseTestArray("*3\r\n:3\r\n+OK\r\n$1\r\na\r\n"), .parsed)
267+
}
268+
269+
func testParsing_array_handlesNullElements() {
270+
XCTAssertEqual(try parseTestArray("*3\r\n:3\r\n$-1\r\n:30\r\n"), .parsed)
271+
}
272+
273+
func testParsing_array_handlesNestedArrays() {
274+
XCTAssertEqual(try parseTestArray("*2\r\n:3\r\n*2\r\n:30\r\n:15\r\n"), .parsed)
275+
}
276+
277+
private func parseTestArray(_ input: String) throws -> RedisDataDecoder._RedisDataDecodingState {
278+
return try parseTestArray(input.convertedToData())
279+
}
280+
281+
private func parseTestArray(_ input: Data) throws -> RedisDataDecoder._RedisDataDecodingState {
282+
var buffer = allocator.buffer(capacity: input.count)
283+
buffer.write(bytes: input)
284+
285+
var position = 1 // "trim" token
286+
287+
return try RedisDataDecoder()._parseArray(at: &position, from: buffer)
288+
}
289+
}
290+
230291
extension RedisDataDecoderParsingTests {
231292
static var allTests = [
232293
("testParsing_with_simpleString", testParsing_with_simpleString),
@@ -235,6 +296,8 @@ extension RedisDataDecoderParsingTests {
235296
("testParsing_with_integer_recursively", testParsing_with_integer_recursively),
236297
("testParsing_with_bulkString", testParsing_with_bulkString),
237298
("testParsing_with_bulkString_recursively", testParsing_with_bulkString_recursively),
299+
("testParsing_with_arrays", testParsing_with_arrays),
300+
("testParsing_with_arrays_recursively", testParsing_with_arrays_recursively),
238301
("testParsing_simpleString_missingEndings_returnsNil", testParsing_simpleString_missingEndings_returnsNil),
239302
("testParsing_simpleString_withNoContent_returnsEmpty", testParsing_simpleString_withNoContent_returnsEmpty),
240303
("testParsing_simpleString_withContent_returnsExpectedContent", testParsing_simpleString_withContent_returnsExpectedContent),
@@ -247,5 +310,11 @@ extension RedisDataDecoderParsingTests {
247310
("testParsing_bulkString_withNull_returnsNil", testParsing_bulkString_withNull_returnsNil),
248311
("testParsing_bulkString_handlesRawBytes", testParsing_bulkString_handlesRawBytes),
249312
("testParsing_bulkString_handlesLargeSizes", testParsing_bulkString_handlesLargeSizes),
313+
("testParsing_array_whenNull_returnsNil", testParsing_array_whenNull_returnsNil),
314+
("testParsing_array_whenEmpty_returnsEmpty", testParsing_array_whenEmpty_returnsEmpty),
315+
("testParsing_array_handlesLargeSizes", testParsing_array_handlesLargeSizes),
316+
("testParsing_array_handlesMixedTypes", testParsing_array_handlesMixedTypes),
317+
("testParsing_array_handlesNullElements", testParsing_array_handlesNullElements),
318+
("testParsing_array_handlesNestedArrays", testParsing_array_handlesNestedArrays),
250319
]
251320
}

0 commit comments

Comments
 (0)