Skip to content

Instantly share code, notes, and snippets.

@nat-n
Created June 3, 2018 20:24
Show Gist options
  • Save nat-n/982fffceb7f838db121bf848e7b37d24 to your computer and use it in GitHub Desktop.
Save nat-n/982fffceb7f838db121bf848e7b37d24 to your computer and use it in GitHub Desktop.
CircularBuffer implementation in Swift 4
public struct CircularBuffer<T>: Collection, CustomStringConvertible {
private var cursor: Int = 0
private var contents: [T] = []
let capacity: Int
public init(capacity: Int) {
self.capacity = capacity
}
public subscript(i: Int) -> T {
get {
if i >= capacity || i < 0 {
fatalError("Index out of range")
}
return contents[(i + cursor) % capacity]
}
set(newValue) {
if i >= capacity || i < 0 {
fatalError("Index out of range")
}
contents[(i + cursor) % capacity] = newValue
}
}
func blaaaa() {
for i in self {
print(i)
}
}
public var count: Int {
return contents.count
}
public var last: T {
return contents[contents.count - 1]
}
private var slice: ArraySlice<T> {
return contents[cursor ..< contents.count] + contents[0 ..< cursor]
}
public var array: [T] {
return Array(slice)
}
public func makeIterator() -> CircularBufferIterator<T> {
return CircularBufferIterator<T>(self)
}
public mutating func append(_ newElement: T) {
if contents.count < capacity {
contents.append(newElement)
return
}
contents[cursor] = newElement
cursor = (cursor + 1) % capacity
}
public mutating func append(contentsOf newElements: [T]) {
let newElemsSlice = newElements[
Swift.max(0, newElements.count - capacity) ..< newElements.count]
if newElements.count >= capacity {
print("__")
contents = Array(newElemsSlice)
cursor = 0
return
}
let countBackFromCursor = cursor - (capacity - newElements.count)
contents = Array(
(countBackFromCursor < 0
? contents[Swift.max(cursor, (contents.count + countBackFromCursor)) ..< contents.count]
: []) +
contents[Swift.max(0, countBackFromCursor) ..< cursor] +
newElemsSlice
)
cursor = Swift.max(0, cursor - contents.count - newElements.count)
}
public mutating func prepend(_ newElement: T) {
if contents.count < capacity {
contents.insert(newElement, at: 0)
return
}
cursor = (cursor == 0 ? capacity : cursor) - 1
contents[cursor] = newElement
}
@discardableResult
public mutating func remove(at: Int) -> T {
if at >= capacity || at < 0 {
fatalError("Index out of range")
}
let index = (at + cursor) % capacity
let result = contents[index]
contents = Array(contents[0..<index] + contents[index + 1 ..< contents.count])
cursor = 0
return result
}
public mutating func removeAll() {
contents = []
cursor = 0
}
public var startIndex: Int { return 0 }
public var endIndex: Int { return contents.count }
public func index(after i: Index) -> Index {
return contents.index(after: i)
}
public func joined(separator: String = ", ") -> String {
return slice.map({ String(describing: $0) }).joined(separator: separator)
}
public var description: String {
return "CircularBuffer<\(String(describing: T.self))>(capacity: \(capacity), [\(joined(separator: ", "))])"
}
// public var debugDescription: String { return "Me!" }
}
public struct CircularBufferIterator<T>: IteratorProtocol {
var circularBuffer: CircularBuffer<T>
var index: Int
public init(_ circularBuffer: CircularBuffer<T>) {
self.circularBuffer = circularBuffer
index = -1
}
public mutating func next() -> T? {
index += 1
if index >= self.circularBuffer.count {
return nil
}
return self.circularBuffer[index]
}
}
import XCTest
class CircularBufferTests: XCTestCase {
var cb: CircularBuffer<Int>!
override func setUp() {
super.setUp()
cb = CircularBuffer<Int>(capacity: 5)
}
func testAppendMoreThanSize() {
cb.append(0)
XCTAssertEqual(cb.array, [0])
cb.append(1)
XCTAssertEqual(cb.array, [0, 1])
cb.append(2)
XCTAssertEqual(cb.array, [0, 1, 2])
cb.append(3)
XCTAssertEqual(cb.array, [0, 1, 2, 3])
cb.append(4)
XCTAssertEqual(cb.array, [0, 1, 2, 3, 4])
cb.append(5)
XCTAssertEqual(cb.array, [1, 2, 3, 4, 5])
cb.append(6)
XCTAssertEqual(cb.array, [2, 3, 4, 5, 6])
}
func testPrependMoreThanSize() {
cb.prepend(0)
XCTAssertEqual(cb.array, [0])
cb.prepend(1)
XCTAssertEqual(cb.array, [1, 0])
cb.prepend(2)
XCTAssertEqual(cb.array, [2, 1, 0])
cb.prepend(3)
XCTAssertEqual(cb.array, [3, 2, 1, 0])
cb.prepend(4)
XCTAssertEqual(cb.array, [4, 3, 2, 1, 0])
cb.prepend(5)
XCTAssertEqual(cb.array, [5, 4, 3, 2, 1])
cb.prepend(6)
XCTAssertEqual(cb.array, [6, 5, 4, 3, 2])
}
func testAlternatingPrependAndAppend() {
cb.append(0)
XCTAssertEqual(cb.array, [0])
cb.prepend(1)
XCTAssertEqual(cb.array, [1, 0])
cb.append(2)
XCTAssertEqual(cb.array, [1, 0, 2])
cb.prepend(3)
XCTAssertEqual(cb.array, [3, 1, 0, 2])
cb.append(4)
XCTAssertEqual(cb.array, [3, 1, 0, 2, 4])
cb.prepend(5)
XCTAssertEqual(cb.array, [5, 3, 1, 0, 2])
cb.append(6)
XCTAssertEqual(cb.array, [3, 1, 0, 2, 6])
}
func testAppendMany() {
cb.append(contentsOf: [0, 1, 2])
XCTAssertEqual(cb.array, [0, 1, 2])
cb.append(contentsOf: [3, 4, 5])
XCTAssertEqual(cb.array, [1, 2, 3, 4, 5])
cb.append(contentsOf: [6, 7, 8])
XCTAssertEqual(cb.array, [4, 5, 6, 7, 8])
cb.append(contentsOf: [10, 11, 12, 13, 14, 15, 16, 17])
XCTAssertEqual(cb.array, [13, 14, 15, 16, 17])
}
func testRemoveAt() {
cb.append(contentsOf: [10, 11, 12, 13, 14])
cb.remove(at: 2)
XCTAssertEqual(cb.array, [10, 11, 13, 14])
cb.append(contentsOf: [15, 16])
cb.remove(at: 2)
XCTAssertEqual(cb.array, [11, 13, 15, 16])
}
func testRemoveAll() {
cb.append(contentsOf: [10, 11, 12, 13, 14])
XCTAssertEqual(cb.array, [10, 11, 12, 13, 14])
XCTAssertEqual(cb.count, 5)
XCTAssertEqual(cb.isEmpty, false)
cb.removeAll()
XCTAssertEqual(cb.array, [])
XCTAssertEqual(cb.count, 0)
XCTAssertEqual(cb.isEmpty, true)
}
func testJoined() {
cb.append(contentsOf: [10, 11, 12, 13, 14])
XCTAssertEqual(cb.joined(), "10, 11, 12, 13, 14")
cb.append(1)
cb.append(2)
XCTAssertEqual(cb.joined(), "12, 13, 14, 1, 2")
}
func testIndexing() {
cb.append(contentsOf: [10, 11, 12, 13, 14])
XCTAssertEqual(cb[0], 10)
cb.prepend(0)
XCTAssertEqual(cb[0], 0)
cb.prepend(1)
XCTAssertEqual(cb[0], 1)
cb.append(1)
XCTAssertEqual(cb[0], 0)
cb.append(2)
XCTAssertEqual(cb.first, 10)
XCTAssertEqual(cb.last, 2)
}
func testIteration() {
cb.append(contentsOf: [10, 11, 12, 13, 14])
cb.append(15)
var expectedContent = [11, 12, 13, 14, 15].makeIterator()
cb.forEach { i in XCTAssertEqual(i, expectedContent.next()) }
}
func testRangeSubscripting() {
cb.append(contentsOf: [10, 11, 12, 13, 14])
cb.append(15)
XCTAssertEqual(Array(cb[1..<3]), [12, 13])
XCTAssertEqual(Array(cb[0...]), [11, 12, 13, 14, 15])
XCTAssertEqual(Array(cb[1...3]), [12, 13, 14])
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment