| /** | 
 |  * Copyright (C) ARM Limited 2010-2014. All rights reserved. | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License version 2 as | 
 |  * published by the Free Software Foundation. | 
 |  */ | 
 |  | 
 | #include "Fifo.h" | 
 |  | 
 | #include <stdlib.h> | 
 | #ifdef WIN32 | 
 | #define valloc malloc | 
 | #endif | 
 |  | 
 | #include "Logging.h" | 
 |  | 
 | // bufferSize is the amount of data to be filled | 
 | // singleBufferSize is the maximum size that may be filled during a single write | 
 | // (bufferSize + singleBufferSize) will be allocated | 
 | Fifo::Fifo(int singleBufferSize, int bufferSize, sem_t* readerSem) { | 
 |   mWrite = mRead = mReadCommit = mRaggedEnd = 0; | 
 |   mWrapThreshold = bufferSize; | 
 |   mSingleBufferSize = singleBufferSize; | 
 |   mReaderSem = readerSem; | 
 |   mBuffer = (char*)valloc(bufferSize + singleBufferSize); | 
 |   mEnd = false; | 
 |  | 
 |   if (mBuffer == NULL) { | 
 |     logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize); | 
 |     handleException(); | 
 |   } | 
 |  | 
 |   if (sem_init(&mWaitForSpaceSem, 0, 0)) { | 
 |     logg->logError(__FILE__, __LINE__, "sem_init() failed"); | 
 |     handleException(); | 
 |   } | 
 | } | 
 |  | 
 | Fifo::~Fifo() { | 
 |   free(mBuffer); | 
 |   sem_destroy(&mWaitForSpaceSem); | 
 | } | 
 |  | 
 | int Fifo::numBytesFilled() const { | 
 |   return mWrite - mRead + mRaggedEnd; | 
 | } | 
 |  | 
 | char* Fifo::start() const { | 
 |   return mBuffer; | 
 | } | 
 |  | 
 | bool Fifo::isEmpty() const { | 
 |   return mRead == mWrite && mRaggedEnd == 0; | 
 | } | 
 |  | 
 | bool Fifo::isFull() const { | 
 |   return willFill(0); | 
 | } | 
 |  | 
 | // Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer | 
 | // 'full' means there is less than singleBufferSize bytes available contiguously; it does not mean there are zero bytes available | 
 | bool Fifo::willFill(int additional) const { | 
 |   if (mWrite > mRead) { | 
 |     if (numBytesFilled() + additional < mWrapThreshold) { | 
 |       return false; | 
 |     } | 
 |   } else { | 
 |     if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | // This function will stall until contiguous singleBufferSize bytes are available | 
 | char* Fifo::write(int length) { | 
 |   if (length <= 0) { | 
 |     length = 0; | 
 |     mEnd = true; | 
 |   } | 
 |  | 
 |   // update the write pointer | 
 |   mWrite += length; | 
 |  | 
 |   // handle the wrap-around | 
 |   if (mWrite >= mWrapThreshold) { | 
 |     mRaggedEnd = mWrite; | 
 |     mWrite = 0; | 
 |   } | 
 |  | 
 |   // send a notification that data is ready | 
 |   sem_post(mReaderSem); | 
 |  | 
 |   // wait for space | 
 |   while (isFull()) { | 
 |     sem_wait(&mWaitForSpaceSem); | 
 |   } | 
 |  | 
 |   return &mBuffer[mWrite]; | 
 | } | 
 |  | 
 | void Fifo::release() { | 
 |   // update the read pointer now that the data has been handled | 
 |   mRead = mReadCommit; | 
 |  | 
 |   // handle the wrap-around | 
 |   if (mRead >= mWrapThreshold) { | 
 |     mRaggedEnd = mRead = mReadCommit = 0; | 
 |   } | 
 |  | 
 |   // send a notification that data is free (space is available) | 
 |   sem_post(&mWaitForSpaceSem); | 
 | } | 
 |  | 
 | // This function will return null if no data is available | 
 | char* Fifo::read(int *const length) { | 
 |   // wait for data | 
 |   if (isEmpty() && !mEnd) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   // obtain the length | 
 |   do { | 
 |     mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite; | 
 |     *length = mReadCommit - mRead; | 
 |   } while (*length < 0); // plugs race condition without using semaphores | 
 |  | 
 |   return &mBuffer[mRead]; | 
 | } |