Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Sources/NIOCore/ByteBuffer-aux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,43 @@ extension ByteBuffer {
return result
}

#if compiler(>=6.2)
@inlinable
@available(macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26, *)
public mutating func readInlineArray<
let count: Int,
IntegerType: FixedWidthInteger
>(
endianness: Endianness = .big,
as: InlineArray<count, IntegerType>.Type = InlineArray<count, IntegerType>.self
) -> InlineArray<count, IntegerType>? {
// use stride to account for padding bytes
let stride = MemoryLayout<IntegerType>.stride
let bytesRequired = stride * count

guard self.readableBytes >= bytesRequired else {
return nil
}

let inlineArray = InlineArray<count, IntegerType> { (outputSpan: inout OutputSpan<IntegerType>) in
for index in 0..<count {
// already made sure of 'self.readableBytes >= bytesRequired' above,
// so this is safe to force-unwrap as it's guaranteed to exist
let integer = self.getInteger(
// this is less than 'bytesRequired' so is safe to multiply
at: stride &* index,
endianness: endianness,
as: IntegerType.self
)!
outputSpan.append(integer)
}
}
// already made sure of 'self.readableBytes >= bytesRequired' above
self._moveReaderIndex(forwardBy: bytesRequired)
return inlineArray
}
#endif

/// Returns the Bytes at the current reader index without advancing it.
///
/// This method is equivalent to calling `getBytes(at: readerIndex, ...)`
Expand Down
61 changes: 61 additions & 0 deletions Tests/NIOCoreTests/ByteBufferTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4461,5 +4461,66 @@ extension ByteBufferTest {
let result = self.buf.readBytes(length: 4)
XCTAssertEqual(Array(0..<4), result!)
}

@available(macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26, *)
func testReadInlineArrayOfUInt8() throws {
let bytes = (0..<10).map { _ in UInt8.random(in: .min ... .max) }

let startWriterIndex = self.buf.writerIndex
let written = self.buf.writeBytes(bytes)
XCTAssertEqual(startWriterIndex + written, self.buf.writerIndex)
XCTAssertEqual(written, self.buf.readableBytes)

let result = try XCTUnwrap(
self.buf.readInlineArray(as: InlineArray<10, UInt8>.self)
)
XCTAssertEqual(10, result.count)
for idx in result.indices {
XCTAssertEqual(bytes[idx], result[idx])
}
XCTAssertEqual(0, self.buf.readableBytes)
XCTAssertEqual(10, self.buf.readerIndex)
}

@available(macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26, *)
func testReadInlineArrayOfUInt64() throws {
let bytes = (0..<15).map { _ in UInt64.random(in: .min ... .max) }

let startWriterIndex = self.buf.writerIndex
var written = 0
for byte in bytes {
written += self.buf.writeInteger(byte)
}
XCTAssertEqual(startWriterIndex + written, self.buf.writerIndex)
XCTAssertEqual(written, self.buf.readableBytes)

let result = try XCTUnwrap(
self.buf.readInlineArray(as: InlineArray<15, UInt64>.self)
)
XCTAssertEqual(15, result.count)
for idx in result.indices {
XCTAssertEqual(bytes[idx], result[idx])
}
XCTAssertEqual(0, self.buf.readableBytes)
XCTAssertEqual(120, self.buf.readerIndex)
}

@available(macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26, *)
func testNotEnoughBytesToReadInlineArrayOfInt32() throws {
let startWriterIndex = self.buf.writerIndex
var written = 0
/// Write 15 bytes. This won't be enough to read an `InlineArray<5, Int32>`.
for _ in 0..<15 {
written += self.buf.writeInteger(UInt8.random(in: .min ... .max))
}
XCTAssertEqual(startWriterIndex + written, self.buf.writerIndex)
XCTAssertEqual(written, self.buf.readableBytes)

let result = self.buf.readInlineArray(as: InlineArray<5, Int32>.self)

XCTAssertNil(result)
XCTAssertEqual(written, self.buf.readableBytes)
XCTAssertEqual(0, self.buf.readerIndex)
}
}
#endif
Loading