Skip to content

Commit

Permalink
add setters to complete implementations of MutableCollection (#1925) (#…
Browse files Browse the repository at this point in the history
…1926)

- for `CircularBuffer` and `MarkedCircularBuffer`.
- with tests.

(cherry picked from commit 5b1f0c6)

Co-authored-by: Guillaume Lessard <[email protected]>
  • Loading branch information
Lukasa and glessard authored Aug 5, 2021
1 parent 87c5323 commit 9a992ee
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 14 deletions.
35 changes: 21 additions & 14 deletions Sources/NIO/CircularBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<Index>) -> 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)
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions Sources/NIO/MarkedCircularBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
1 change: 1 addition & 0 deletions Tests/NIOTests/CircularBufferTests+XCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ extension CircularBufferTests {
("testReplaceSubrangeWithSubrangeLargerThanTargetRange", testReplaceSubrangeWithSubrangeLargerThanTargetRange),
("testReplaceSubrangeSameSize", testReplaceSubrangeSameSize),
("testReplaceSubrangeReplaceBufferWithEmptyArray", testReplaceSubrangeReplaceBufferWithEmptyArray),
("testRangeSubscriptExpanding", testRangeSubscriptExpanding),
("testWeCanDistinguishBetweenEmptyAndFull", testWeCanDistinguishBetweenEmptyAndFull),
("testExpandZeroBasedRingWorks", testExpandZeroBasedRingWorks),
("testExpandNonZeroBasedRingWorks", testExpandNonZeroBasedRingWorks),
Expand Down
17 changes: 17 additions & 0 deletions Tests/NIOTests/CircularBufferTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,23 @@ class CircularBufferTests: XCTestCase {
XCTAssertTrue(ring.isEmpty)
}

func testRangeSubscriptExpanding() {
var ring = CircularBuffer<Int>(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..<ring.endIndex].count, 2)
ring[index..<ring.endIndex] = [10,11,12,13,14,15,16,17,18,19]
XCTAssertTrue(ring.testOnly_verifyInvariantsForNonSlices())
XCTAssertEqual(originalCount + 8, ring.count)
XCTAssertEqual(4, ring.first)
XCTAssertEqual(19, ring.last)
}

func testWeCanDistinguishBetweenEmptyAndFull() {
var ring = CircularBuffer<Int>(initialCapacity: 4)
XCTAssertTrue(ring.isEmpty)
Expand Down
1 change: 1 addition & 0 deletions Tests/NIOTests/MarkedCircularBufferTests+XCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extension MarkedCircularBufferTests {
("testFirst", testFirst),
("testCount", testCount),
("testSubscript", testSubscript),
("testRangeSubscript", testRangeSubscript),
("testIsEmpty", testIsEmpty),
("testPopFirst", testPopFirst),
]
Expand Down
12 changes: 12 additions & 0 deletions Tests/NIOTests/MarkedCircularBufferTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ class MarkedCircularBufferTests: XCTestCase {
XCTAssertEqual(buf[buf.index(buf.startIndex, offsetBy: 3)], 4)
}

func testRangeSubscript() throws {
var buf = MarkedCircularBuffer<Int>(initialCapacity: 4)
for i in 1...4 {
buf.append(i)
}
let range = buf.startIndex..<buf.index(buf.startIndex, offsetBy: 2)
XCTAssertEqual(buf[range].count, 2)
buf[range] = [0,1]
XCTAssertEqual(buf.firstIndex(of: 2), nil)
XCTAssertEqual(buf.count, 4)
}

func testIsEmpty() throws {
var buf = MarkedCircularBuffer<Int>(initialCapacity: 4)
for i in 1...4 {
Expand Down

0 comments on commit 9a992ee

Please sign in to comment.