class {:autocontracts} RingBuffer { // public view of the class: ghost var Contents: seq // the contents of the ring buffer ghost var N: nat // the capacity of the ring buffer // private implementation: var data: array var start: nat var count: nat // Valid encodes the consistency of RingBuffer objects (think, invariant) predicate Valid() { data != null && data.Length == N && N > 0 && (N == 0 ==> count == start == 0 && Contents == []) && (N != 0 ==> count <= N && start < N) && Contents == if start + count <= N then data[start..start+count] else data[start..] + data[..start+count-N] } constructor (n: nat) requires n > 0 ensures Contents == [] && N == n { data := new T[n]; start, count := 0, 0; Contents, N := [], n; } method Clear() ensures Contents == [] && N == old(N) { count := 0; Contents := []; } method Head() returns (x: T) requires Contents != [] ensures x == Contents[0] { x := data[start]; } method Enqueue(x: T) //requires |Contents| != N ensures Contents == old(Contents) + [x] && N >= old(N) { if count == data.Length { var more := data.Length; var d := new T[data.Length + more]; forall i | 0 <= i < data.Length { d[if i < start then i else i + more] := data[i]; } N, data, start := N + more, d, start + more; } var nextEmpty := if start + count < data.Length then start + count else start + count - data.Length; data[nextEmpty] := x; count := count + 1; Contents := Contents + [x]; } method Dequeue() returns (x: T) requires Contents != [] ensures x == old(Contents)[0] && Contents == old(Contents)[1..] && N == old(N) { x := data[start]; //assert x == Contents[0]; start, count := if start + 1 == data.Length then 0 else start + 1, count - 1; Contents := Contents[1..]; } } method TestHarness(x: int, y: int, z: int) { var b := new RingBuffer(2); b.Enqueue(x); b.Enqueue(y); var h := b.Dequeue(); assert h == x; b.Enqueue(z); h := b.Dequeue(); assert h == y; h := b.Dequeue(); assert h == z; }