Skip to content

Commit

Permalink
Add conditional conformances of CircularBuffer to Equatable and Hashable
Browse files Browse the repository at this point in the history
Motivation:
In general, Swift collections and sequences automatically conforms to
Equatable and Hashable when their Element conforms to Equatable and
Hashhable, respectively. This wasn't the case of CircularBuffer, which
was unintuitive and could cause useless boilerplate code.

Modifications:
CircularBuffer has been extended so that it conforms to Equatable when
its Element is Equatable, and to Hashable when its element is Hashable.
Corresponding test cases have been added.

Result:
Appropriate conditional conformances of CircularBuffer to Equatable and
Hashable
  • Loading branch information
PopFlamingo committed Aug 9, 2019
1 parent 8c6c83c commit fee7195
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Sources/NIO/CircularBuffer.swift
Expand Up @@ -692,3 +692,17 @@ extension CircularBuffer {
return self.verifyInvariants() && self.unreachableAreNil()
}
}

extension CircularBuffer: Equatable where Element: Equatable {
public static func ==(lhs: CircularBuffer, rhs: CircularBuffer) -> Bool {
lhs.count == rhs.count && zip(lhs, rhs).allSatisfy(==)
}
}

extension CircularBuffer: Hashable where Element: Hashable {
public func hash(into hasher: inout Hasher) {
for element in self {
hasher.combine(element)
}
}
}
2 changes: 2 additions & 0 deletions Tests/NIOTests/CircularBufferTests+XCTest.swift
Expand Up @@ -73,6 +73,8 @@ extension CircularBufferTests {
("testLotsOfInsertAtEnd", testLotsOfInsertAtEnd),
("testPopLast", testPopLast),
("testModify", testModify),
("testEquality", testEquality),
("testHash", testHash),
]
}
}
Expand Down
59 changes: 59 additions & 0 deletions Tests/NIOTests/CircularBufferTests.swift
Expand Up @@ -912,4 +912,63 @@ class CircularBufferTests: XCTestCase {
}
XCTAssertEqual([0, 5, 2, 3], Array(buf))
}

func testEquality() {
// Empty buffers
let emptyA = CircularBuffer<Int>()
let emptyB = CircularBuffer<Int>()
XCTAssertEqual(emptyA, emptyB)

var buffA = CircularBuffer<Int>()
var buffB = CircularBuffer<Int>()
var buffC = CircularBuffer<Int>()
var buffD = CircularBuffer<Int>()
buffA.append(contentsOf: 1...10)
buffB.append(contentsOf: 1...10)
buffC.append(contentsOf: 2...11) // Same count different values
buffD.append(contentsOf: 1...2) // Different count
XCTAssertEqual(buffA, buffB)
XCTAssertNotEqual(buffA, buffC)
XCTAssertNotEqual(buffA, buffD)

// Will make internal head/tail indexes different
var prependBuff = CircularBuffer<Int>()
var appendBuff = CircularBuffer<Int>()
for i in (1...100).reversed() {
prependBuff.prepend(i)
}
for i in 1...100 {
appendBuff.append(i)
}
// But the contents are still the same
XCTAssertEqual(prependBuff, appendBuff)
}

func testHash() {
let emptyA = CircularBuffer<Int>()
let emptyB = CircularBuffer<Int>()
XCTAssertEqual(Set([emptyA,emptyB]).count, 1)

var buffA = CircularBuffer<Int>()
var buffB = CircularBuffer<Int>()
buffA.append(contentsOf: 1...10)
buffB.append(contentsOf: 1...10)
XCTAssertEqual(Set([buffA,buffB]).count, 1)
buffB.append(123)
XCTAssertEqual(Set([buffA,buffB]).count, 2)
buffA.append(1)
XCTAssertEqual(Set([buffA,buffB]).count, 2)

// Will make internal head/tail indexes different
var prependBuff = CircularBuffer<Int>()
var appendBuff = CircularBuffer<Int>()
for i in (1...100).reversed() {
prependBuff.prepend(i)
}
for i in 1...100 {
appendBuff.append(i)
}
XCTAssertEqual(Set([prependBuff,appendBuff]).count, 1)
}

}

0 comments on commit fee7195

Please sign in to comment.