Package simtools.util

Source Code of simtools.util.StreamMemoryBuffer$StreamReader

/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info:  http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2006, by :
*     Corporate:
*         EADS Astrium SAS
*         EADS CRC
*     Individual:
*         Claude Cazenave
*
* $Id: StreamMemoryBuffer.java,v 1.12 2007/12/04 15:33:10 booba_skaya Exp $
*
* Changes
* -------
* 16 janv. 2006 : Initial public release (CC);
*
*/
package simtools.util;

import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* A memory buffer to be filled by one writer and read by several readers
* The readers do not empty the buffer, they acces data in it using an index
* The writer is filling the circular buffer it writes at a given index and increments
* a minimum index value to prevent access from the readers on overwritten data due
* to the cyclic buffer. Thus the writer is never constrained by the readers. The
* readers have to be fast enough to read the buffer. The buffer is allocated
* into native memory and thus can be quite large.
* This implementation is based on the assumption that packets size has a maximum value
* and that the minimum value is not too much different from the maximum value.
* If not efficient in terms of memory usage, it is assumed that a subclass will manage it.
*
* @author cazenave_c
*
*/
public class StreamMemoryBuffer {
    // a logger for debug purpose
    static Logger logger = simtools.util.LogConfigurator.getLogger(StreamMemoryBuffer.class.getName());
  /**
   * The memory circular buffer
   */
  protected ByteBuffer buffer;

  /**
   * A circular array with packets length
   */
  protected int[] lengths;

  /**
   * The size of the circular buffer= number of packets in memory
   */
  protected final long bufferSize;

  /**
   * The maximum size for one packet
   */
  protected final int streamMaxSize;

  /**
   * The index of the next packet to be written
   */
  protected long index;

  /**
   * The last availble packet index (-1 at the beginning)
   */
  protected long minIndex;

  /**
   * The numbe of times the circular buffer is filled (starting at 1)
   */
  protected int counter;

  /**
   * A syncho to allow readers to synchrosize on the buffer updates
   */
  protected Object synchro=new Object();

  /**
   * Create a new memory buffer
   * @param bufferSize the number of packets to store
   * @param streamMaxSize the max size for one packet
   */
  public StreamMemoryBuffer(int bufferSize, int streamMaxSize){
    this.bufferSize=bufferSize;
    this.streamMaxSize=streamMaxSize;
    lengths=new int[bufferSize];
    //Use logger to track FINE message.
        //Use logger to track CONFIG message.
        logger.config("New StreamMemory buffer to size with bufferSize * streamMaxSize = ("+bufferSize+"*"+streamMaxSize+")="+(bufferSize*(streamMaxSize)));
    buffer=ByteBuffer.allocate(bufferSize*(streamMaxSize));
    index=0;
        minIndex = -1;
    counter=0;
  }

  /**
   * Write a packet into the buffer
   * @param b The array from which bytes are to be read
   * @param offset The offset within the array of the first byte to be read
   * @param length The number of bytes to be read from the given array
   * @return the written packet index
   */
  public long write(byte[] b, int offset, int length){
    if(length>streamMaxSize){
      throw new IllegalArgumentException(""+length+">"+streamMaxSize);
    }
    if((index-counter*bufferSize)==0){
      buffer.position(0);
      counter++;
    }
    if(counter>1){
      minIndex++;
    }
    lengths[(int)(index-(counter-1)*bufferSize)]=length;
    buffer.put(b, offset, length);
    if(length<streamMaxSize){
      buffer.position(buffer.position()+streamMaxSize-length);
    }

    synchronized(synchro){
      index++;
      synchro.notifyAll();
    }
    return index-1;
  }

  /**
   * Write a byte buffer into the buffer
   * @param b the byte buffer
   * @return the written packet index
   */
  public long write(ByteBuffer b){
    int length=b.remaining();
    if(length>streamMaxSize){
      throw new IllegalArgumentException(""+length+">"+streamMaxSize);
    }
    if((index-counter*bufferSize)==0){
      buffer.position(0);
      counter++;
    }
    if(counter>1){
      minIndex++;
    }
    lengths[(int)(index-(counter-1)*bufferSize)]=length;
    buffer.put(b);
    if(length<streamMaxSize){
        //Log only if FINEST is loggable.
            if (logger.isLoggable(Level.FINEST)) {
                //Use logger to track FINEST message.
                logger.finest("Set position to : (position + streamMaxSize - length)=" +
                    "("+buffer.position()+"+"+streamMaxSize+"-"+length
                    +")="+(buffer.position()+streamMaxSize-length));
            }
      buffer.position(buffer.position()+streamMaxSize-length);
    }

    synchronized(synchro){
      index++;
      synchro.notifyAll();
    }
    return index-1;
  }

  /**
   * Create a reader on this buffer
   * This method is overwritten to implement specific readers
   * @return the new reader
   */
  public StreamReader createReader(){
    return new StreamReader();
  }

  /**
   * A buffer reader
   */
  public class StreamReader{

    /**
     * The read buffer : an independent view on the write buffer
     */
    final protected ByteBuffer readBuffer;

    /**
     * The mark used to perform the writing of the buffer into a channel or
     * into packet buffers
     */
    protected long mark;


    /**
     * Create a reader
     */
    protected StreamReader(){
      readBuffer=buffer.asReadOnlyBuffer();
      mark=minIndex;
    }

    /**
     * Wait for the next writing on the buffer and return the index of
     * this packet. The index is equal to previous index if timeout ellapses.
     * The retuned index is negative if nothing to read or in case of
     * thread interruption.
     * @param timeout the wait timeout
     * @return the index of the last packet to read
     */
    public long getNextReadIndex(long timeout, long lastReadIndex){
      synchronized(synchro){
        if(lastReadIndex<(index-1)){
          return index-1;
        }
        try {
          synchro.wait(timeout);
        } catch (InterruptedException e) {
          return -1;
        }
        return index-1;
      }
    }

    /**
     * Set the mark location equal to the last received packet
     */
    public void resetMark(){
      mark=getReadIndex();
    }

    /**
     * Set the mark location wrt the oldest packet to read
     * The mark is initialized with last available index at reader
     * creation time.
     * To start saving while a unknown number of packets is already
     * written the mark can be set with a margin relative to
     * the oldest packet to read index. For instance margin can be
     * equal to bufferSize/2
     * @param margin the mark location relative to the oldest available packet
     */
    public void resetMark(int margin){
      mark=minIndex+margin;
    }

    /**
     * Bufferize the buffer content from the mark to packet buffer
     * @param a packet factory to address the right buffers according
     * to the key read from the packet
     * @param timeout the timeout to wait
     * @return the number of written bytes
     * @throws IOException
     * @throws PacketBufferValidationError
     */
    public long bufferize(PacketBufferFactory factory, long timeout) throws IOException, PacketBufferValidationError{
      long res=0;
      long r=getNextReadIndex(timeout,mark);
      if(mark>r){
        return res;
      }
      if(mark<minIndex){
        throw new BufferUnderflowException();
      }
      int k;
            if(mark < 0){
                mark = -1;
            }
            k = (int)((mark+1)%bufferSize);

            int c=(int)(r-mark);
      for(int i=0;i<c;i++){
        int x=(k+i)%(int) bufferSize;
        readBuffer.limit(x*streamMaxSize+lengths[x]);
        readBuffer.position(x*streamMaxSize);
        ByteBuffer bb=readBuffer.slice();
        try {
          factory.add(bb);
        } catch (PacketBufferValidationError e) {
          validationErrorControl(e);
        }
        res+=lengths[x];
      }
      if(mark<minIndex){
        throw new BufferUnderflowException();
      }
      mark=r;
      return res;
    }

    /**
     * Default managment of a packet validation error is to send the
     * exception at upper level
     *
     * @param e the validation error
     * @throws PacketBufferValidationError
     */
    protected void validationErrorControl(PacketBufferValidationError e)throws PacketBufferValidationError{
      throw e;
    }

    /**
     * Save the buffer content from the mark to the last written packet
     * @param channel the channel to write into
     * @param timeout the timeout to wait
     * @return the number of written bytes
     * @throws IOException
     */
    public long save(ByteChannel channel, long timeout) throws IOException{
      long res=0;
      long r=getNextReadIndex(timeout,mark);
      if(mark>r){
        return res;
      }
      if(mark<minIndex){
        throw new BufferUnderflowException();
      }
            int k;
            if(mark < 0){
                k = 0;
            }else{
                k = (int)(mark%bufferSize);
            }
      int c=(int)(r-mark);
      for(int i=0;i<c;i++){
        int x=k+i;
        if(x==bufferSize) x=0;
        readBuffer.limit(x*streamMaxSize+lengths[x]);
        readBuffer.position(x*streamMaxSize);
        channel.write(readBuffer);
        res+=lengths[x];
      }
      if(mark<minIndex){
        throw new BufferUnderflowException();
      }
      mark=r;
      return res;
    }

    /**
     * Read a packet
     * @param readIndex the index of the packet to read
     * @param readOffset the offset inside this packet
     * @param readLength the number of bytes to read or
     * -1 if it is up to the packet end
     * @param b the array to which bytes are to be written
     * @param offset the offset within the array of the first byte to be written
     * @return the number of bytes read
     */
    public int read(long readIndex, int readOffset, int readLength,
        byte[] b, int offset){
      if(readIndex<minIndex){
        throw new BufferUnderflowException();
      }
      if(readIndex>=index){
        throw new BufferOverflowException();
      }
      int k=(int)(readIndex%bufferSize);
      readBuffer.position(k*streamMaxSize+readOffset);
      int l=lengths[k];
      if(readLength<0){
        l-=readOffset;
        readBuffer.get(b,offset,l);
      }
      else{
        if(l-readOffset<readLength){
          throw new BufferOverflowException();
        }
        l=readLength;
        readBuffer.get(b,offset,l);
      }

      // cross check for concurent access
      if(readIndex<minIndex){
        throw new BufferUnderflowException();
      }
      return l;
    }

    /**
     * Get the packet length
     * @param readIndex the index of the packet
     * @return the number of bytes in this packet
     */
    public int getLength(int readIndex){
      if(readIndex<minIndex){
        throw new BufferUnderflowException();
      }
      if(readIndex>=index){
        throw new BufferOverflowException();
      }
      int l=lengths[(int)(readIndex%bufferSize)];
      // cross check for concurent access
      if(readIndex<minIndex){
        throw new BufferUnderflowException();
      }
      return l;
    }

    /**
     * Get the last packet index
     * @return the index
     */
    public long getReadIndex(){
      return index-1;
    }

    /**
     * Get the oldest packet index
     * @return the index
     */
    public long getMinIndex(){
      return minIndex;
    }
  }
   
    /**
     * Clean the StreamMemoryBuffer
     */
    public void clean() {
        lengths=null;
        buffer = null;
        synchro = null;
    }
}
TOP

Related Classes of simtools.util.StreamMemoryBuffer$StreamReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.