Skip to content

Commit f2ad915

Browse files
authored
[perf] Manually implement lastIndex(where:) in ByteBufferView (#3413)
### Motivation: `ByteBuffer.lastIndex(where:)` is of suboptimal performance. The default Collection implementations don't go through any "magic underscored" functions like `_customIndexOfEquatableElement`. ### Modifications: Manually implement `lastIndex(where:)`. ### Result: Basically free performance boost. 2x+ boost even for not big buffers of a few hundred bytes. This function is currently used in `ByteBufferView.trim(limitingElements:)`. I have no immediate use case for this function, but it's still an issue worth addressing.
1 parent 7124f09 commit f2ad915

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

Sources/NIOCore/ByteBuffer-views.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,22 @@ extension ByteBufferView: RangeReplaceableCollection {
259259
try ptr.firstIndex(where: predicate).map { $0 + self._range.lowerBound }
260260
}
261261
}
262+
263+
/// Returns the index of the last byte that matches the given predicate.
264+
///
265+
/// - Parameter predicate: A closure that takes a byte as its argument
266+
/// and returns a Boolean value that indicates whether the passed byte
267+
/// represents a match.
268+
/// - Returns: The index of the last byte in the collection that matches
269+
/// `predicate`, or `nil` if no bytes match.
270+
///
271+
/// - Complexity: O(*n*), where *n* is the length of the collection.
272+
@inlinable
273+
public func lastIndex(where predicate: (UInt8) throws -> Bool) rethrows -> Index? {
274+
try self.withUnsafeBytes { ptr in
275+
try ptr.lastIndex(where: predicate).map { $0 + self._range.lowerBound }
276+
}
277+
}
262278
}
263279

264280
extension ByteBuffer {

Tests/NIOCoreTests/ByteBufferTest.swift

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,17 +2423,37 @@ class ByteBufferTest: XCTestCase {
24232423
func testBufferViewLastIndex() {
24242424
self.buf.clear()
24252425
self.buf.writeBytes(Array(repeating: UInt8(0x4E), count: 1024))
2426-
self.buf.setBytes([UInt8(0x59)], at: 1000)
2427-
self.buf.setBytes([UInt8(0x59)], at: 1001)
2428-
self.buf.setBytes([UInt8(0x59)], at: 1022)
2429-
self.buf.setBytes([UInt8(0x59)], at: 3)
2430-
self.buf.setBytes([UInt8(0x3F)], at: 1023)
24312426
self.buf.setBytes([UInt8(0x3F)], at: 2)
2427+
self.buf.setBytes([UInt8(0x59)], at: 3)
2428+
self.buf.setBytes([UInt8(0x61)], at: 1000)
2429+
self.buf.setBytes([UInt8(0x59)], at: 1001)
2430+
self.buf.setBytes([UInt8(0x61)], at: 1002)
2431+
self.buf.setBytes([UInt8(0x59)], at: 1003)
2432+
self.buf.setBytes([UInt8(0x61)], at: 1004)
2433+
self.buf.setBytes([UInt8(0x59)], at: 1023)
2434+
self.buf.setBytes([UInt8(0x3F)], at: 1024)
24322435
let view = self.buf.viewBytes(at: 5, length: 1010)
2433-
XCTAssertEqual(1001, view?.lastIndex(of: UInt8(0x59)))
2436+
XCTAssertEqual(1003, view?.lastIndex(of: UInt8(0x59)))
24342437
XCTAssertNil(view?.lastIndex(of: UInt8(0x3F)))
24352438
}
24362439

2440+
func testBufferViewLastIndexWithWhereClosure() {
2441+
self.buf.clear()
2442+
self.buf.writeBytes(Array(repeating: UInt8(0x4E), count: 1024))
2443+
self.buf.setBytes([UInt8(0x3F)], at: 2)
2444+
self.buf.setBytes([UInt8(0x59)], at: 3)
2445+
self.buf.setBytes([UInt8(0x61)], at: 1000)
2446+
self.buf.setBytes([UInt8(0x59)], at: 1001)
2447+
self.buf.setBytes([UInt8(0x61)], at: 1002)
2448+
self.buf.setBytes([UInt8(0x59)], at: 1003)
2449+
self.buf.setBytes([UInt8(0x61)], at: 1004)
2450+
self.buf.setBytes([UInt8(0x59)], at: 1023)
2451+
self.buf.setBytes([UInt8(0x3F)], at: 1024)
2452+
let view = self.buf.viewBytes(at: 5, length: 1010)
2453+
XCTAssertEqual(1003, view?.lastIndex(where: { $0 == UInt8(0x59) }))
2454+
XCTAssertNil(view?.lastIndex(where: { $0 == UInt8(0x3F) }))
2455+
}
2456+
24372457
func testBufferViewContains() {
24382458
self.buf.clear()
24392459
self.buf.writeBytes(Array(repeating: UInt8(0x4E), count: 1024))

0 commit comments

Comments
 (0)