| /** @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; | |
| } |