code: /**
   * Copyright (c) 2007, Regents of the University of California
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * Redistributions of source code must retain the above copyright notice,
   * this list of conditions and the following disclaimer.
   *
   * Redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution.
   *
   * Neither the name of the University of California, Los Angeles nor the
   * names of its contributors may be used to endorse or promote products
   * derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * Created Oct 10, 2007
   */
  package avrora.sim.state;
  
  import java.util.Arrays;
  
  /**
   * The <code>FIFO</code> class implements a FIFO (queue) of bytes of a fixed size.
   * It detects overflow and underflow and has sticky flags that can be queried for
   * these conditions and cleared.
   *
   * @author Ben L. Titzer
   */
  public class ByteFIFO {
  
      protected final byte[] data;
  
      protected int head;
      protected int used;
  
      protected int save_head;
      protected int save_used;
    
      protected boolean underflow;
      protected boolean overflow;
  
      /**
       * The default constructor for this class creates a new FIFO with the specified size.
       *
       * @param sz the size of the FIFO queue, in bytes
       */
      public ByteFIFO(int sz) {
          data = new byte[sz];
      }
  
      /**
       * The <code>add()</code> method adds a byte to this FIFO at the end. If the FIFO is
       * not yet full, the byte will be successfully added. If the FIFO is already full,
       * then this method will set the overflow flag which can be queried with the
       * <code>overflow()</code> method.
       *
       * @param c the byte to add to this FIFO
       */
      public void add(byte c) {
          if (full()) {
              overflow = true;
          } else data[wrap(head + used++)] = c;
      }
  
      /**
       * The <code>remove()</code> method removes a byte from this FIFO, if one exists. If
       * the FIFO contains elements, the first element entered into the queue is removed
       * and returned. If the queue is empty, this this method sets the underflow flag
       * which can be queried with the <code>underflow()</code> method.
       *
       * @return the next byte in the FIFO if it exists; <code>0</code> otherwise
       */
      public byte remove() {
          byte b = 0;
          if (empty()) {
              underflow = true;
          } else {
              b = data[head];
              head = wrap(head + 1);
              used--;
          }
          return b;
      }
  
      /**
       * Gets the byte at absolute position in FIFO
       * @param ind Absolute position
       * @return byte at position ind from beginning of FIFO
       */
      public byte getAbsoluteByte(int ind) {
          return data[ind];
      }
      
      /**
       * Sets the byte at absolute position in FIFO and returns old value
       * @param ind Absolute position
       * @param val New value
       * @return Old values
       */
      public byte setAbsoluteByte(int ind, byte val) {
      	byte prev = data[ind];
          data[ind] = val;
          return prev;
      }
      
      /**
       * Get the byte relative to a packet, which starts at the saved head.
       * Thus, clear() or saveState() has to be used at the beginning of a packet.
       * @param ind Position relative to saved head
       * @return byte at the given position
       */
      public byte getRelativeByte(int ind) {
          return data[wrap(head + ind)];
      }
  
      /**
       * Get a series of bytes relative to a packet, which starts at the saved head.
       * Thus, clear() or saveState() has to be used at the beginning of a packet.
       * @param from Start position relative to saved head
       * @param to End position relative to saved head
       * @return byte array with the values in the given range
       */
      public byte[] getRelativeField(int from, int to) {
          if (((head + from) < data.length) && ((head + to) == (data.length)))
              return copyOfRange(data, wrap(head + from), (head + to));
          else if (((head + from) < data.length) && ((head + to) > (data.length))) {
              byte[] r = new byte[to - from];
              System.arraycopy(data, wrap(head + from), r, 0, (data.length - (head + from)));
              System.arraycopy(data, 0, r, (data.length - (head + from)), wrap(head + to));
              return r;
          } else return copyOfRange(data, wrap(head + from), wrap(head + to));
      }
  
      /**
       * The <code>underFlow()</code> method returns the status of the underflow flag,
       * which occurs when a <code>remove()</code> occurs on an empty FIFO.
       * The underflow flag is cleared by the <code>clear()</code> and <code>clearFlags()</code>
       * methods.
       *
       * @return true if this FIFO has experienced an underflow condition in a previous call;
       *         false otherwise
       */
      public boolean underFlow() {
          return underflow;
      }
  
      /**
       * The <code>overFlow()</code> method returns the status of the overflow flag, which
       * is set when an <code>add()</code> call occurs on a full FIFO.
       * The overflow flag is cleared by the <code>clear()</code> and <code>clearFlags()</code>
       * methods.
       *
       * @return true if this FIFO has experienced an underflow condition in a previous call;
       *         false otherwise
       */
      public boolean overFlow() {
          return overflow;
      }
  
      public int capacity() {
          return data.length;
      }
  
      public int size() {
          return used;
      }
  
      public boolean full() {
          return used == data.length;
      }
  
      public boolean empty() {
          return used == 0;
      }
  
      public void clearFlags() {
          overflow = false;
          underflow = false;
      }
  
      public void saveState() {
        save_head = head;
        save_used = used;
      }
      
      public void refill() {
          overflow = false;
          underflow = false;
          head = save_head;
          used = save_used;        
      }
      
      public void dropLast() {
          // how many bytes have been read since saveState()?
          int readBytes = head >= save_head ? head - save_head : data.length-save_head+head;
          if (readBytes >= save_used) {  // more bytes read than were available at saveState() time
              used = 0;  // now it's empty
          }
          else {
              used = save_used - readBytes;  // drop those that have been added since saveState()
          }
      }
  
      public void clear() {
          overflow = false;
          underflow = false;
          save_head = head = 0;
          save_used = used = 0;
          Arrays.fill(data, (byte) 0);
      }
  
      private int wrap(int i) {
          if (i >= data.length) return i - data.length;
          return i;
      }
  
      public static byte[] copyOfRange(byte[] origin, int from, int to) {
          byte[] narray = new byte[to - from];
          for (int i = 0; i < narray.length; i++) {
              narray[i] = origin[from + i];
          }
          return narray;
      }
  }
  