Skip to content

Instantly share code, notes, and snippets.

@rayfix
Created August 31, 2014 14:56
Show Gist options
  • Save rayfix/1fb467981e386ad5797e to your computer and use it in GitHub Desktop.
Save rayfix/1fb467981e386ad5797e to your computer and use it in GitHub Desktop.
Swift Ring Buffer
//
// Ring.swift
//
// Copyright (c) 2014 Pelfunc, Inc. All rights reserved.
//
/// Allow iteration of Ring<T> in LIFO order
public struct RingGenerator<T> : GeneratorType {
public mutating func next() -> T?
{
if remainingCount > 0 {
let value = buffer[index%buffer.count]
--remainingCount
++index
return value
}
return nil
}
private init(_ ring:Ring<T>!) {
buffer = ring.buffer
remainingCount = ring.storedCount
index = (ring.count-remainingCount) % ring.capacity
}
private let buffer:[T]
private var index:Int
private var remainingCount:Int
}
/// A ring keeps track of the last n objects where n is set
/// using capacity. The ring can be iterated on in LIFO
/// order.
public class Ring<T> : SequenceType {
/// Where the buffer is stored.
final private var buffer:[T] = []
/// The total number of times add() has been called since
/// init or the last reset.
public private(set) var count:Int = 0
/// The number of objects this ring can store.
public let capacity:Int = 0
/// The number of objects store in the ring.
public var storedCount:Int {
return min(count, capacity)
}
/// Create an empty ring with the specified capacity
/// @requires capacity must be > 0
public init(capacity:Int) {
assert(capacity > 0)
self.capacity = capacity
}
/// Add value to the ring.
public func add(value:T) {
if !isFull {
buffer.append(value)
}
else {
buffer[count % buffer.count] = value
}
++count
}
/// The value that will be removed when a new values is
/// added to the ring.
public var willRemoveValue:T? {
if isFull {
let index = (count-storedCount) % capacity
return buffer[index]
}
return nil
}
/// Set the storedCount to zero.
public func reset() {
count = 0
buffer = []
}
/// Return true if the ring is empty
public var isEmpty:Bool {
return count == 0
}
/// Return true if the ring is full and newly added values will bump out old ones.
public var isFull:Bool {
return count >= capacity
}
/// Allow iteration from oldest to newest value.
public func generate() -> RingGenerator<T> {
return RingGenerator(self)
}
}
@rayfix
Copy link
Author

rayfix commented Aug 31, 2014

Shortly after posting, I realized I forgot to buffer.reserveCapacity(capacity) in init(). Important for efficiency with large rings. Something for the next rev.

@wlaurance
Copy link

What would it take to add in the reserveCapacity?

@ilanl
Copy link

ilanl commented Jul 1, 2015

How to make it thread safe ?

@iachievedit
Copy link

@rayfix Did you intend to mark this reserving all rights, which actually implies that individuals do not have the right to copy it? I'd like to use this code in an application (as well as highlight it on a blog post), but I take copyright notices seriously and cannot currently without infringing. Would you mind updating it to an MIT/ISC/BSD-style license?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment