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 return 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
|
|
}
|