diff --git a/Sources/NIO/CircularBuffer.swift b/Sources/NIO/CircularBuffer.swift index 9bf378957a..3a204ef50c 100644 --- a/Sources/NIO/CircularBuffer.swift +++ b/Sources/NIO/CircularBuffer.swift @@ -227,14 +227,6 @@ extension CircularBuffer: Collection, MutableCollection { // MARK: RandomAccessCollection implementation extension CircularBuffer: RandomAccessCollection { /// Returns the index offset by `distance` from `index`. - @inlinable - public func index(_ i: Index, offsetBy distance: Int) -> Index { - return .init(backingIndex: (i.backingIndex &+ distance) & self.mask, - backingCount: self.count, - backingIndexOfHead: self.headBackingIndex) - } - - /// Returns an index that is the specified distance from the given index. /// /// The following example obtains an index advanced four positions from a /// string's starting index and then prints the character at that position. @@ -261,15 +253,30 @@ extension CircularBuffer: RandomAccessCollection { /// - Complexity: O(1) if the collection conforms to /// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the absolute /// value of `distance`. + @inlinable + public func index(_ i: Index, offsetBy distance: Int) -> Index { + return .init(backingIndex: (i.backingIndex &+ distance) & self.mask, + backingCount: self.count, + backingIndexOfHead: self.headBackingIndex) + } + @inlinable public subscript(bounds: Range) -> SubSequence { - precondition(self.distance(from: self.startIndex, to: bounds.lowerBound) >= 0) - precondition(self.distance(from: bounds.upperBound, to: self.endIndex) >= 0) + get { + precondition(self.distance(from: self.startIndex, to: bounds.lowerBound) >= 0) + precondition(self.distance(from: bounds.upperBound, to: self.endIndex) >= 0) - var newRing = self - newRing.headBackingIndex = bounds.lowerBound.backingIndex - newRing.tailBackingIndex = bounds.upperBound.backingIndex - return newRing + var newRing = self + newRing.headBackingIndex = bounds.lowerBound.backingIndex + newRing.tailBackingIndex = bounds.upperBound.backingIndex + return newRing + } + set { + precondition(self.distance(from: self.startIndex, to: bounds.lowerBound) >= 0) + precondition(self.distance(from: bounds.upperBound, to: self.endIndex) >= 0) + + self.replaceSubrange(bounds, with: newValue) + } } } diff --git a/Sources/NIO/MarkedCircularBuffer.swift b/Sources/NIO/MarkedCircularBuffer.swift index 70f9356068..4c9d0b4f67 100644 --- a/Sources/NIO/MarkedCircularBuffer.swift +++ b/Sources/NIO/MarkedCircularBuffer.swift @@ -161,6 +161,15 @@ extension MarkedCircularBuffer: Collection, MutableCollection { get { return self._buffer[bounds] } + set { + var index = bounds.lowerBound + var iterator = newValue.makeIterator() + while let newElement = iterator.next(), index != bounds.upperBound { + self._buffer[index] = newElement + formIndex(after: &index) + } + precondition(iterator.next() == nil && index == bounds.upperBound) + } } } diff --git a/Tests/NIOTests/CircularBufferTests+XCTest.swift b/Tests/NIOTests/CircularBufferTests+XCTest.swift index 2d7d64197c..c000919e66 100644 --- a/Tests/NIOTests/CircularBufferTests+XCTest.swift +++ b/Tests/NIOTests/CircularBufferTests+XCTest.swift @@ -42,6 +42,7 @@ extension CircularBufferTests { ("testReplaceSubrangeWithSubrangeLargerThanTargetRange", testReplaceSubrangeWithSubrangeLargerThanTargetRange), ("testReplaceSubrangeSameSize", testReplaceSubrangeSameSize), ("testReplaceSubrangeReplaceBufferWithEmptyArray", testReplaceSubrangeReplaceBufferWithEmptyArray), + ("testRangeSubscriptExpanding", testRangeSubscriptExpanding), ("testWeCanDistinguishBetweenEmptyAndFull", testWeCanDistinguishBetweenEmptyAndFull), ("testExpandZeroBasedRingWorks", testExpandZeroBasedRingWorks), ("testExpandNonZeroBasedRingWorks", testExpandNonZeroBasedRingWorks), diff --git a/Tests/NIOTests/CircularBufferTests.swift b/Tests/NIOTests/CircularBufferTests.swift index 144c313bcc..7574932583 100644 --- a/Tests/NIOTests/CircularBufferTests.swift +++ b/Tests/NIOTests/CircularBufferTests.swift @@ -325,6 +325,23 @@ class CircularBufferTests: XCTestCase { XCTAssertTrue(ring.isEmpty) } + func testRangeSubscriptExpanding() { + var ring = CircularBuffer(initialCapacity: 4) + for idx in 0..<5 { + ring.prepend(idx) + } + XCTAssertEqual(5, ring.count) + + let index = ring.firstIndex(of: 1)! + let originalCount = ring.count + XCTAssertEqual(ring[index..(initialCapacity: 4) XCTAssertTrue(ring.isEmpty) diff --git a/Tests/NIOTests/MarkedCircularBufferTests+XCTest.swift b/Tests/NIOTests/MarkedCircularBufferTests+XCTest.swift index 2552a3d4cc..5e7781b988 100644 --- a/Tests/NIOTests/MarkedCircularBufferTests+XCTest.swift +++ b/Tests/NIOTests/MarkedCircularBufferTests+XCTest.swift @@ -35,6 +35,7 @@ extension MarkedCircularBufferTests { ("testFirst", testFirst), ("testCount", testCount), ("testSubscript", testSubscript), + ("testRangeSubscript", testRangeSubscript), ("testIsEmpty", testIsEmpty), ("testPopFirst", testPopFirst), ] diff --git a/Tests/NIOTests/MarkedCircularBufferTests.swift b/Tests/NIOTests/MarkedCircularBufferTests.swift index 71129e6b21..c6a1f3ff86 100644 --- a/Tests/NIOTests/MarkedCircularBufferTests.swift +++ b/Tests/NIOTests/MarkedCircularBufferTests.swift @@ -117,6 +117,18 @@ class MarkedCircularBufferTests: XCTestCase { XCTAssertEqual(buf[buf.index(buf.startIndex, offsetBy: 3)], 4) } + func testRangeSubscript() throws { + var buf = MarkedCircularBuffer(initialCapacity: 4) + for i in 1...4 { + buf.append(i) + } + let range = buf.startIndex..(initialCapacity: 4) for i in 1...4 {