/** @file | |
Class for arbitrary sized FIFO queues. | |
The FIFO is empty if both the Read and Write indexes are equal. | |
The FIFO is full if the next write would make the Read and Write indexes equal. | |
Member variable NumElements is the maximum number of elements that can be | |
contained in the FIFO. | |
If NumElements is ZERO, there is an error. | |
NumElements should be in the range 1:N. | |
Members WriteIndex and ReadIndex are indexes into the array implementing the | |
FIFO. They should be in the range 0:(NumElements - 1). | |
One element of the FIFO is always reserved as the "terminator" element. Thus, | |
the capacity of a FIFO is actually NumElements-1. | |
Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials are licensed and made available | |
under the terms and conditions of the BSD License which accompanies this | |
distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php. | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <Uefi.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <LibConfig.h> | |
#include <assert.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <wchar.h> | |
#include <Containers/Fifo.h> | |
/** Determine number of items available to read from the FIFO. | |
The number of items are either the number of bytes, or the number of elements | |
depending upon the value of the As enumerator. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[in] As An enumeration variable whose value determines whether the | |
returned value is the number of bytes or the number of elements | |
currently contained by the FIFO. | |
@retval 0 The FIFO is empty. | |
@retval >=0 The number of items contained by the FIFO. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_NumInQueue ( | |
cFIFO *Self, | |
FIFO_ElemBytes As | |
) | |
{ | |
size_t Count; | |
if(Self->ReadIndex <= Self->WriteIndex) { | |
Count = Self->WriteIndex - Self->ReadIndex; | |
} | |
else { | |
Count = Self->NumElements - (Self->ReadIndex - Self->WriteIndex); | |
} | |
if(As == AsBytes) { | |
Count *= Self->ElementSize; | |
} | |
return Count; | |
} | |
/** Determine amount of free space in the FIFO that can be written into. | |
The number of items are either the number of bytes, or the number of elements | |
depending upon the value of the As enumerator. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[in] As An enumeration variable whose value determines whether the | |
returned value is the number of bytes or the number of elements | |
currently available in the FIFO. | |
@retval 0 The FIFO is full. | |
@retval >=0 The number of items which can be accepted by the FIFO. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_FreeSpace ( | |
cFIFO *Self, | |
FIFO_ElemBytes As | |
) | |
{ | |
size_t Count; | |
UINT32 RDex; | |
UINT32 WDex; | |
RDex = Self->ReadIndex; | |
WDex = Self->WriteIndex; | |
if(RDex <= WDex) { | |
Count = (Self->NumElements - (WDex - RDex)) - 1; | |
} | |
else { | |
Count = (RDex - WDex)-1; | |
} | |
if(As == AsBytes) { | |
Count *= Self->ElementSize; | |
} | |
return Count; | |
} | |
/** Reduce the FIFO contents by NumElem elements. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[in] NumElem Number of elements to delete from the FIFO. | |
@retval 0 FIFO is now empty. | |
@retval N>0 There are still N elements in the FIFO. | |
@retval -1 There are fewer than NumElem elements in the FIFO. | |
**/ | |
static | |
ssize_t | |
FIFO_Reduce ( | |
cFIFO *Self, | |
size_t NumElem | |
) | |
{ | |
size_t QCount; | |
ssize_t RetVal; | |
assert(Self != NULL); | |
QCount = FIFO_NumInQueue(Self, AsElements); | |
if(NumElem > QCount) { | |
RetVal = -1; | |
errno = EINVAL; | |
} | |
else { | |
RetVal = (ssize_t)ModuloAdd(Self->ReadIndex, (UINT32)NumElem, Self->NumElements); | |
Self->ReadIndex = (UINT32)RetVal; | |
RetVal = (ssize_t)(QCount - NumElem); | |
} | |
return RetVal; | |
} | |
/** Test whether the FIFO is empty. | |
@param[in] Self Pointer to the FIFO instance. | |
@retval TRUE The FIFO is empty. | |
@retval FALSE There is data in the FIFO. | |
**/ | |
static | |
BOOLEAN | |
EFIAPI | |
FIFO_IsEmpty ( | |
cFIFO *Self | |
) | |
{ | |
assert(Self != NULL); | |
return (BOOLEAN)(Self->WriteIndex == Self->ReadIndex); | |
} | |
/** Test whether the FIFO is full. | |
@param[in] Self Pointer to the FIFO instance. | |
@retval TRUE The FIFO is full. | |
@retval FALSE There is free space in the FIFO. | |
**/ | |
static | |
BOOLEAN | |
EFIAPI | |
FIFO_IsFull ( | |
cFIFO *Self | |
) | |
{ | |
assert(Self != NULL); | |
return (BOOLEAN)(ModuloIncrement(Self->WriteIndex, Self->NumElements) == (INT32)Self->ReadIndex); | |
} | |
/** Add one or more elements to the FIFO. | |
This function allows one to add one or more elements, as specified by Count, | |
to the FIFO. Each element is of the size specified when the FIFO object | |
was instantiated (FIFO.ElementSize). | |
pElement points to the first byte of the first element to be added. | |
If multiple elements are to be added, the elements are expected to be | |
organized as a packed array. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[in] pElement Pointer to the element(s) to enqueue (add). | |
@param[in] Count Number of elements to add. | |
@retval 0 The FIFO is full. | |
@retval >=0 The number of elements added to the FIFO. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_Enqueue ( | |
cFIFO *Self, | |
const void *pElement, | |
size_t Count | |
) | |
{ | |
uintptr_t ElemPtr; | |
uintptr_t QPtr; | |
size_t i; | |
UINT32 SizeOfElement; | |
UINT32 Windex; | |
assert(Self != NULL); | |
assert(pElement != NULL); | |
if(FIFO_IsFull(Self)) { // FIFO is full so can't add to it | |
Count = 0; // Zero characters added | |
} | |
else { // Otherwise, FIFO is not full... | |
Count = MIN(Count, Self->FreeSpace(Self, AsElements)); // Smaller of requested or available space | |
SizeOfElement = Self->ElementSize; // Size of Elements, in bytes | |
Windex = Self->WriteIndex; // Index of first writable slot in FIFO | |
ElemPtr = (uintptr_t)pElement; // Addr. of element to add, as an integer | |
QPtr = (uintptr_t)Self->Queue + (SizeOfElement * Windex); // Addr. in FIFO to write, as an integer | |
for(i = 0; i < Count; ++i) { // For Count elements... | |
(void)CopyMem((void *)QPtr, (const void *)ElemPtr, SizeOfElement); // Copy an element into the FIFO | |
Windex = (UINT32)ModuloIncrement(Windex, Self->NumElements); // Increment the Write index, wrap if necessary | |
if(Windex == 0) { // If the index wrapped | |
QPtr = (uintptr_t)Self->Queue; // Go to the beginning | |
} | |
else { | |
QPtr += SizeOfElement; // Otherwise, point to next in FIFO | |
} | |
ElemPtr += SizeOfElement; // And also point to next Element to add | |
} | |
Self->WriteIndex = Windex; // Finally, save the new Write Index | |
} | |
return Count; // Number of elements added to FIFO | |
} | |
/** Read or copy elements from the FIFO. | |
This function allows one to read one or more elements, as specified by Count, | |
from the FIFO. Each element is of the size specified when the FIFO object | |
was instantiated (FIFO.ElementSize). | |
pElement points to the destination of the first byte of the first element | |
to be read. If multiple elements are to be read, the elements are expected | |
to be organized as a packed array. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[out] pElement Pointer to where to store the element(s) read from the FIFO. | |
@param[in] Count Number of elements to dequeue. | |
@param[in] Consume If TRUE, consume read elements. Otherwise, preserve. | |
@retval 0 The FIFO is empty. | |
@retval >=0 The number of elements read from the FIFO. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_Dequeue ( | |
cFIFO *Self, | |
void *pElement, | |
size_t Count, | |
BOOLEAN Consume | |
) | |
{ | |
UINTN QPtr; | |
UINT32 RDex; | |
UINT32 SizeOfElement; | |
UINT32 i; | |
assert(Self != NULL); | |
assert(pElement != NULL); | |
if(FIFO_IsEmpty(Self)) { | |
Count = 0; | |
} | |
else { | |
RDex = Self->ReadIndex; // Get this FIFO's Read Index | |
SizeOfElement = Self->ElementSize; // Get size of this FIFO's elements | |
Count = MIN(Count, Self->Count(Self, AsElements)); // Lesser of requested or actual | |
QPtr = (UINTN)Self->Queue + (RDex * SizeOfElement); // Point to Read location in FIFO | |
for(i = 0; i < Count; ++i) { // Iterate Count times... | |
(void)CopyMem(pElement, (const void *)QPtr, SizeOfElement); // Copy element from FIFO to caller's buffer | |
RDex = (UINT32)ModuloIncrement(RDex, Self->NumElements); // Increment Read Index | |
if(RDex == 0) { // If the index wrapped | |
QPtr = (UINTN)Self->Queue; // Point back to beginning of data | |
} | |
else { // Otherwise | |
QPtr += SizeOfElement; // Point to the next element in FIFO | |
} | |
pElement = (char*)pElement + SizeOfElement; // Point to next element in caller's buffer | |
} // Iterate: for loop | |
if(Consume) { // If caller requests data consumption | |
Self->ReadIndex = RDex; // Set FIFO's Read Index to new Index | |
} | |
} | |
return Count; // Return number of elements actually read | |
} | |
/** Read elements from the FIFO. | |
Read the specified number of elements from the FIFO, removing each element read. | |
The number of elements actually read from the FIFO is returned. This number can differ | |
from the Count requested if more elements are requested than are in the FIFO. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[out] pElement Pointer to where to store the element read from the FIFO. | |
@param[in] Count Number of elements to dequeue. | |
@retval 0 The FIFO is empty. | |
@retval >=0 The number of elements read from the FIFO. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_Read ( | |
cFIFO *Self, | |
void *pElement, | |
size_t Count | |
) | |
{ | |
return FIFO_Dequeue(Self, pElement, Count, TRUE); | |
} | |
/** Make a copy of the FIFO's data. | |
The contents of the FIFO is copied out and linearized without affecting the | |
FIFO contents. This function is idempotent. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[out] pElement Pointer to where to store the elements copied from the FIFO. | |
@param[in] Count Number of elements to copy. | |
@retval 0 The FIFO is empty. | |
@retval >=0 The number of elements copied from the FIFO. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_Copy ( | |
cFIFO *Self, | |
void *pElement, | |
size_t Count | |
) | |
{ | |
return FIFO_Dequeue(Self, pElement, Count, FALSE); | |
} | |
/** Get the FIFO's current Read Index. | |
@param[in] Self Pointer to the FIFO instance. | |
**/ | |
static | |
UINT32 | |
EFIAPI | |
FIFO_GetRDex ( | |
cFIFO *Self | |
) | |
{ | |
assert(Self != NULL); | |
return Self->ReadIndex; | |
} | |
/** Get the FIFO's current Write Index. | |
@param[in] Self Pointer to the FIFO instance. | |
@return The current value of the FIFO's WriteIndex member is returned. | |
**/ | |
static | |
UINT32 | |
EFIAPI | |
FIFO_GetWDex ( | |
cFIFO *Self | |
) | |
{ | |
assert(Self != NULL); | |
return Self->WriteIndex; | |
} | |
/** Cleanly delete a FIFO instance. | |
@param[in] Self Pointer to the FIFO instance. | |
**/ | |
static | |
void | |
EFIAPI | |
FIFO_Delete ( | |
cFIFO *Self | |
) | |
{ | |
assert(Self != NULL); | |
if(Self->Queue != NULL) { | |
FreePool(Self->Queue); | |
Self->Queue = NULL; // Zombie catcher | |
} | |
FreePool(Self); | |
} | |
/** Empty the FIFO, discarding up to NumToFlush elements. | |
@param[in] Self Pointer to the FIFO instance. | |
@param[in] NumToFlush Number of elements to flush from the FIFO. | |
If larger than the number of elements in the | |
FIFO, the FIFO is emptied. | |
@return Returns the number of elements remaining in the FIFO after the flush. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_Flush ( | |
cFIFO *Self, | |
size_t NumToFlush | |
) | |
{ | |
size_t NumInQ; | |
size_t Remainder; | |
assert(Self != NULL); | |
NumInQ = FIFO_NumInQueue(Self, AsElements); | |
if(NumToFlush >= NumInQ) { | |
Self->ReadIndex = 0; | |
Self->WriteIndex = 0; | |
Remainder = 0; | |
} | |
else { | |
Remainder = FIFO_Reduce(Self, NumToFlush); | |
} | |
return Remainder; | |
} | |
/** Remove the most recently added element from the FIFO. | |
@param[in] Self Pointer to the FIFO instance. | |
@return Returns the number of elements remaining in the FIFO. | |
**/ | |
static | |
size_t | |
EFIAPI | |
FIFO_Truncate ( | |
cFIFO *Self | |
) | |
{ | |
size_t Remainder; | |
assert(Self != NULL); | |
Remainder = FIFO_NumInQueue(Self, AsElements); | |
if(Remainder > 0) { | |
Self->WriteIndex = (UINT32)ModuloDecrement(Self->WriteIndex, Self->NumElements); | |
--Remainder; | |
} | |
return Remainder; | |
} | |
/** Construct a new instance of a FIFO Queue. | |
@param[in] NumElements Number of elements to be contained in the new FIFO. | |
@param[in] ElementSize Size, in bytes, of an element. | |
@retval NULL Unable to create the instance. | |
@retval NonNULL Pointer to the new FIFO instance. | |
**/ | |
cFIFO * | |
EFIAPI | |
New_cFIFO( | |
UINT32 NumElements, | |
size_t ElementSize | |
) | |
{ | |
cFIFO *FIFO; | |
UINT8 *Queue; | |
FIFO = NULL; | |
if((NumElements > 2) && (ElementSize > 0)) { | |
FIFO = (cFIFO *)AllocatePool(sizeof(cFIFO)); | |
if(FIFO != NULL) { | |
Queue = (UINT8 *)AllocateZeroPool(NumElements * ElementSize); | |
if(Queue != NULL) { | |
FIFO->Write = FIFO_Enqueue; | |
FIFO->Read = FIFO_Read; | |
FIFO->Copy = FIFO_Copy; | |
FIFO->IsEmpty = FIFO_IsEmpty; | |
FIFO->IsFull = FIFO_IsFull; | |
FIFO->Count = FIFO_NumInQueue; | |
FIFO->FreeSpace = FIFO_FreeSpace; | |
FIFO->Flush = FIFO_Flush; | |
FIFO->Truncate = FIFO_Truncate; | |
FIFO->Delete = FIFO_Delete; | |
FIFO->GetRDex = FIFO_GetRDex; | |
FIFO->GetWDex = FIFO_GetWDex; | |
FIFO->Queue = Queue; | |
FIFO->ElementSize = (UINT32)ElementSize; | |
FIFO->NumElements = (UINT32)NumElements; | |
FIFO->ReadIndex = 0; | |
FIFO->WriteIndex = 0; | |
} | |
else { | |
FreePool(FIFO); | |
FIFO = NULL; | |
} | |
} | |
} | |
return FIFO; | |
} |