mirror of https://bitbucket.org/ausocean/av.git
167 lines
4.4 KiB
Go
167 lines
4.4 KiB
Go
/*
|
|
NAME
|
|
RingBuffer.go - a structure that encapsulates a RingBuffer datastructure with conccurency
|
|
functionality
|
|
|
|
DESCRIPTION
|
|
See Readme.md
|
|
|
|
AUTHOR
|
|
Saxon Nelson-Milton <saxon.milton@gmail.com>
|
|
|
|
LICENSE
|
|
RingBuffer.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean)
|
|
|
|
It is free software: you can redistribute it and/or modify them
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
option) any later version.
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
|
|
*/
|
|
|
|
package ringbuffer
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
)
|
|
|
|
/*
|
|
RingBuffer aims to provide functionality of a RingBuffer data structure.
|
|
It may be used in concurrent routines.
|
|
*/
|
|
type RingBuffer struct {
|
|
memory [][]byte
|
|
size int
|
|
noOfElements int
|
|
first int
|
|
last int
|
|
currentlyWriting bool
|
|
currentlyReading bool
|
|
sizeChannel chan int
|
|
mutex sync.mutex
|
|
}
|
|
|
|
/*
|
|
Make allocates the required memory to the RingBuffer memory attribute and
|
|
initialises other attributes.
|
|
*/
|
|
func (rb *RingBuffer) Make(size int, elementsize int) {
|
|
rb.memory = make([][]byte, size)
|
|
for i := range rb.memory {
|
|
rb.memory[i] = make([]byte, elementsize)
|
|
}
|
|
rb.size = size
|
|
rb.noOfElements = 0
|
|
rb.first = -1
|
|
rb.last = -1
|
|
rb.currentlyWriting = false
|
|
rb.currentlyReading = false
|
|
rb.sizeChannel = make(chan int, size)
|
|
rb.mutex = sync.mutex{}
|
|
}
|
|
|
|
/*
|
|
Get provides the address to the next empty element in the RingBuffer.
|
|
An error is returned if the buffer is full, or there has already been
|
|
a call to Get without an accompanying call to DoneWriting.
|
|
*/
|
|
func (rb *RingBuffer) Get() ([]byte, error) {
|
|
rb.mutex.Lock()
|
|
defer rb.mutex.Unlock()
|
|
if rb.noOfElements == rb.size {
|
|
return nil, errors.New("Buffer full!")
|
|
}
|
|
if rb.currentlyWriting {
|
|
return nil, errors.New("Second call to Get! Call DoneWriting first!")
|
|
}
|
|
rb.currentlyWriting = true
|
|
nextlast := rb.last + 1
|
|
if nextlast == rb.size {
|
|
nextlast = 0
|
|
}
|
|
return rb.memory[nextlast], nil
|
|
}
|
|
|
|
/*
|
|
DoneWriting let's the RingBuffer know that we have finished writing to the
|
|
address recieved by a call to Get, and also let's the buffer know how much
|
|
data was written. An Error is thrown if there was not an initial call to Get.
|
|
*/
|
|
func (rb *RingBuffer) DoneWriting(size int) error {
|
|
rb.mutex.Lock()
|
|
defer rb.mutex.Unlock()
|
|
if !rb.currentlyWriting {
|
|
return errors.New("DoneWriting called without initial call to Get!")
|
|
}
|
|
if rb.first == -1 {
|
|
rb.first++
|
|
}
|
|
rb.sizeChannel <- size
|
|
rb.last++
|
|
if rb.last == rb.size {
|
|
rb.last = 0
|
|
}
|
|
rb.noOfElements++
|
|
rb.currentlyWriting = false
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
Read gets the address of the next element that we would like to get data
|
|
from and thereafter consider empty in the buffer for writing during latter
|
|
calls to Get. The address of the data is returned, as well as the size of
|
|
the data contained at this address. An error is returned if the buffer is
|
|
empty, or there has been a second call to Read before a call to DoneReading.
|
|
*/
|
|
func (rb *RingBuffer) Read() ([]byte, int, error) {
|
|
rb.mutex.Lock()
|
|
defer rb.mutex.Unlock()
|
|
if !rb.CanRead() {
|
|
return nil, 0, errors.New("Buffer is empty, nothging to read!")
|
|
}
|
|
if rb.currentlyReading {
|
|
return nil, 0, errors.New("Second call to Read! Call DoneReading first!")
|
|
}
|
|
rb.currentlyReading = true
|
|
return rb.memory[rb.first], <-rb.sizeChannel, nil
|
|
}
|
|
|
|
/*
|
|
DoneReading informs the buffer that we have finished with the address
|
|
provided by Read. The buffer can now consider this address to be empty.
|
|
An error is returned if there was no initial call to Read.
|
|
*/
|
|
func (rb *RingBuffer) DoneReading() error {
|
|
rb.mutex.Lock()
|
|
defer rb.mutex.Unlock()
|
|
if !rb.currentlyReading {
|
|
return errors.New("DoneReading called but no initial call to Read!")
|
|
}
|
|
rb.first++
|
|
if rb.first == rb.size {
|
|
rb.first = 0
|
|
}
|
|
rb.noOfElements--
|
|
rb.currentlyReading = false
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
CanRead returns true if it is possible to read from the buffer, i.e. if
|
|
it is not empty.
|
|
*/
|
|
func (rb *RingBuffer) CanRead() bool {
|
|
if rb.first == -1 || rb.noOfElements == 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|