PICTools Programmer's Reference
Appendix 1: Circular Queue Processing

Pegasus does not require all the input be available at the same time. Similarly, it doesn't require that there be enough space for all of the output data. For some applications, it makes sense to use smaller input and/or output buffers to conserve memory. For example, an application which reads a BMP file and compresses it to a JPEG file, may not wish to process the entire buffer before getting more data. The algorithm might require an entire line of data at a time so a partial line would not be processed. Instead, a RES_GET_NEED_DATA would be called to get the rest of the line. In cases like this, the Get and Put queues can be used as circular queues. The queue has a data supplier and a data consumer. The data supplier adds data to the rear of the queue. The data consumer removes data from the front of the queue. The data consumer will only change the Front pointer. The data supplier will only change the Rear pointer, except that the data supplier may change both the Front and Rear pointers to point to the start of the buffer whenever they are equal. It is important to note that a buffer when used as a circular queue HAS ONE LESS BYTE in it than might be expected. This is because an empty queue is denoted by having Front == Rear. Without an extra byte, when the buffer fills up the Rear pointer would be advanced to the Front pointer, i.e., the Front pointer would equal the Rear pointer. This ambiguous situation where Front == Rear implies the buffer is completely empty or completely full, is resolved by having an extra byte in the buffer. In this case a full buffer is denoted when the Rear pointer is logically one less than the Front pointer.

The queue is empty.

Initially, no data are in the queue.

 

The queue is filled.

Next, the data supplier fills the queue with data.

 

Some data is taken from the queue.

The data consumer takes some data out of the queue.

 

More data is added to the queue.

The data supplier fills the queue again.

 

More data is taken from the queue.

The data consumer takes more data out of the queue.

More data is again added to the queue.

The data supplier fills the queue in two steps. First the queue is filled from Rear to End.

 

Then the queue is filled from Start.

Circular Queue Code Example

Following is a circular get queue example showing the Forward case. The Get Queue initialization code looks like this:

 
Copy Code

      // Get queue initialization

      // allow for one byte more so Front == Rear means empty

      pbQBuffer = malloc(dwQDataDesired + 1);

      PicParm.Get.Start = pbQBuffer;

      PicParm.Get.End   = pbQBuffer + dwQDataDesired + 1;

      PicParm.Get.Front = PicParm.Get.Start;

      PicParm.Get.Rear   = PicParm.Get.Front;

 

The application code that will be invoked when the opcode requires more data to be added to its Get Queue is presented below. In this code, we assume that the data being copied to the Get Queue exists in a buffer tracked by these two global variables:

 

      BYTE *InputData      : beginning of the available data

      DWORD InputDataLength: current size of available data

 

Every time the code is invoked, InputData will grow until it reaches the end of available data. Conversely InputDataLength will decrease until it reaches zero meaning all data has been consumed. 

We can assume that this code will not be called if the Get Queue is full; therefore, there are 2 scenarios that the code needs to deal with:

  1. The buffer is empty. We know by checking if Rear==Front. In this case, just fill the buffer.
  2. The buffer is partially full. In this case there are two possible situations. We determine which one it is by looking at the relative position of Front and Rear:
    1. Rear is before Front: the empty space (to be filled with data) is contiguous. In this case, we need to fill the empty space from Rear to Front-1.

       

       

       

       

       

    2. Front is before Rear: the data in the buffer is contiguous. In this case, we need to to fill the empty spaces: Rear to End and Start to Front-1

       

       

 

 

 

The code to add data to the Get Queue looks like this:   

 
Copy Code

if (PicParm.Get.Front == PicParm.Get.Rear) {

            // this is the empty queue scenario...

            PicParm.Get.Front = PicParm.Get.Rear = PicParm.Get.Start;

            CopyLen = PicParm.Get.End - PicParm.Get.Start - 1;

            CopyToRear_And_AdjustInput(PicParm, CopyLen);

      } else { // partially full scenario

            if (PicParm.Get.Rear < PicParm.Get.Front) {

                  // empty space is contiguous: rear->front-1

                  CopyLen = PicParm.Get.Front - 1 - PicParm.Get.Rear;

               CopyToRear_And_AdjustInput(PicParm, CopyLen);

            } else {

                  // empty space has 2 segments... rear->end

                  CopyLen = PicParm.Get.End - PicParm.Get.Rear;

               CopyToRear_And_AdjustInput(PicParm, CopyLen);

                  // start->front-1

                  PicParm.Get.Rear = PicParm.Get.Start;

                  CopyLen = PicParm.Get.Front - 1 - PicParm.Get.Rear;

               CopyToRear_And_AdjustInput(PicParm, CopyLen);

            }

      }

The utility function that copies from the input buffer into the queue and adjusts InputData and InputDataLength as needed, is shown below:

 
Copy Code

voidCopyToRear_And_AdjustInput(PIC_PARM PicParm, DWORD CopyLen)

{

      if ( CopyLen > InputDataLength )

            CopyLen = InputDataLength;

      memcpy(PicParm.Get.Rear, InputData, CopyLen);

      InputData += CopyLen;

      InputDataLength -= CopyLen;

      PicParm.Get.Rear += CopyLen;

}

 

 

 


©2022. Accusoft Corporation. All Rights Reserved.

Send Feedback