mirror of https://bitbucket.org/ausocean/av.git
167 lines
4.3 KiB
Go
167 lines
4.3 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 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 rb 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 that buffer that we have finished with the address
|
||
|
provided by Read. The buffer can now consider rb 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
|
||
|
}
|