mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
datatypes: adding ringbuffer (#15818)
This commit is contained in:
parent
09411128af
commit
391ac12fe2
124
vlib/datatypes/ringbuffer.v
Normal file
124
vlib/datatypes/ringbuffer.v
Normal file
@ -0,0 +1,124 @@
|
||||
// Written by flopetautschnig (floscodes) (c) 2022
|
||||
|
||||
module datatypes
|
||||
|
||||
// RingBuffer - public struct that represents the ringbuffer
|
||||
pub struct RingBuffer<T> {
|
||||
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<T>(s int) RingBuffer<T> {
|
||||
return RingBuffer<T>{
|
||||
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<T>) 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<T>) 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<T>) 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<T>) 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<T>) 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<T>) 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<T>) 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<T>) clear() {
|
||||
rb = RingBuffer<T>{
|
||||
content: []T{len: rb.content.len, cap: rb.content.cap}
|
||||
}
|
||||
}
|
||||
|
||||
// occupied - returns occupied capacity of the buffer.
|
||||
pub fn (rb RingBuffer<T>) 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<T>) 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<T>) move_reader() {
|
||||
rb.reader++
|
||||
if rb.reader > rb.content.len - 1 {
|
||||
rb.reader = 0
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut rb RingBuffer<T>) move_writer() {
|
||||
rb.writer++
|
||||
if rb.writer > rb.content.len - 1 {
|
||||
rb.writer = 0
|
||||
}
|
||||
}
|
65
vlib/datatypes/ringbuffer_test.v
Normal file
65
vlib/datatypes/ringbuffer_test.v
Normal file
@ -0,0 +1,65 @@
|
||||
import datatypes
|
||||
|
||||
fn test_push_and_pop() {
|
||||
mut r := datatypes.new_ringbuffer<int>(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<int>(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<int>(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<int>(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<int>(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
|
||||
}
|
Loading…
Reference in New Issue
Block a user