/* NAME RingBuffer.go - a structure that encapsulates a RingBuffer datastructure with conccurency functionality DESCRIPTION See Readme.md AUTHOR Saxon Nelson-Milton 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 }