diff --git a/vlib/datatypes/ringbuffer.v b/vlib/datatypes/ringbuffer.v new file mode 100644 index 0000000000..3e608d80de --- /dev/null +++ b/vlib/datatypes/ringbuffer.v @@ -0,0 +1,124 @@ +// Written by flopetautschnig (floscodes) (c) 2022 + +module datatypes + +// RingBuffer - public struct that represents the ringbuffer +pub struct RingBuffer { +mut: + reader int // index of the tail where data is going to be read + writer int // index of the head where data is going to be written + content []T +} + +// new_ringbuffer - creates an empty ringbuffer +pub fn new_ringbuffer(s int) RingBuffer { + return RingBuffer{ + content: []T{len: s + 1, cap: s + 1} + } // increasing custom set size by one element in order to make ring flow possible, so that writer cannot equal reader before reader-index has been read. +} + +// push - adds an element to the ringbuffer +pub fn (mut rb RingBuffer) push(element T) ? { + if rb.is_full() { + return error('Buffer overflow') + } else { + rb.content[rb.writer] = element + rb.move_writer() + } +} + +// pop - returns the oldest element of the buffer +pub fn (mut rb RingBuffer) pop() ?T { + mut v := rb.content[rb.reader] + if rb.is_empty() { + return error('Buffer is empty') + } else { + rb.move_reader() + } + return v +} + +// push_many - pushes an array to the buffer +pub fn (mut rb RingBuffer) push_many(elements []T) ? { + for v in elements { + rb.push(v) or { return err } + } +} + +// pop_many - returns a given number(n) of elements of the buffer starting with the oldest one +pub fn (mut rb RingBuffer) pop_many(n u64) ?[]T { + mut elements := []T{} + for _ in 0 .. n { + elements << rb.pop() or { return err } + } + return elements +} + +// is_empty - checks if the ringbuffer is empty +pub fn (rb RingBuffer) is_empty() bool { + return rb.reader == rb.writer // if reader equals writer it means that no value to read has been written before. It follows that the buffer is empty. +} + +// is_full - checks if the ringbuffer is full +pub fn (rb RingBuffer) is_full() bool { + if rb.writer + 1 == rb.reader { + return true + } else if rb.writer == rb.content.len - 1 && rb.reader == 0 { + return true + } else { + return false + } +} + +// capacity - returns the capacity of the ringbuffer +pub fn (rb RingBuffer) capacity() int { + return rb.content.cap - 1 // reduce by one because of the extra element explained in function `new_ringbuffer()` +} + +// clear - emptys the ringbuffer and all pushed elements +pub fn (mut rb RingBuffer) clear() { + rb = RingBuffer{ + content: []T{len: rb.content.len, cap: rb.content.cap} + } +} + +// occupied - returns occupied capacity of the buffer. +pub fn (rb RingBuffer) occupied() int { + mut reader := rb.reader + mut v := 0 + if rb.is_empty() { + return v + } + for { + reader++ + if reader > rb.content.len - 1 { + reader = 0 + } + v++ + if reader == rb.writer { + break + } + } + return v +} + +// remaining - returns remaining capacity of the buffer +pub fn (rb RingBuffer) remaining() int { + return rb.capacity() - rb.occupied() +} + +// head an tail-pointer move methods +// these methods are not public, they are just an eneasement for handling the pointer-movement process. +fn (mut rb RingBuffer) move_reader() { + rb.reader++ + if rb.reader > rb.content.len - 1 { + rb.reader = 0 + } +} + +fn (mut rb RingBuffer) move_writer() { + rb.writer++ + if rb.writer > rb.content.len - 1 { + rb.writer = 0 + } +} diff --git a/vlib/datatypes/ringbuffer_test.v b/vlib/datatypes/ringbuffer_test.v new file mode 100644 index 0000000000..8607d0f050 --- /dev/null +++ b/vlib/datatypes/ringbuffer_test.v @@ -0,0 +1,65 @@ +import datatypes + +fn test_push_and_pop() { + mut r := datatypes.new_ringbuffer(2) + + r.push(3) or { panic(err) } + r.push(4) or { panic(err) } + + mut oldest_value := r.pop() or { 0 } + + assert oldest_value == 3 + + r.push(5) or { panic(err) } + + oldest_value = r.pop() or { 0 } + + assert oldest_value == 4 +} + +fn test_clear_and_empty() { + mut r := datatypes.new_ringbuffer(4) + r.push(3) or { panic(err) } + r.push(4) or { panic(err) } + + oldest_value := r.pop() or { 0 } + assert oldest_value == 3 + + r.clear() + + assert r.is_empty() == true +} + +fn test_capacity_and_is_full() { + mut r := datatypes.new_ringbuffer(4) + + assert r.capacity() == 4 + + r.push(3) or { panic(err) } + r.push(4) or { panic(err) } + r.push(5) or { panic(err) } + r.push(6) or { panic(err) } + + assert r.is_full() == true +} + +fn test_occupied_and_remaining() { + mut r := datatypes.new_ringbuffer(4) + + r.push(3) or { panic(err) } + r.push(4) or { panic(err) } + + assert r.occupied() == r.remaining() +} + +fn test_push_and_pop_many() { + mut r := datatypes.new_ringbuffer(4) + a := [1, 2, 3, 4] + r.push_many(a) or { panic(err) } + + assert r.is_full() == true + + b := r.pop_many(4) or { panic(err) } + + assert a == b +}