Java/File Input Output/Buffering

Материал из Java эксперт
Версия от 18:01, 31 мая 2010; (обсуждение)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Buffered copying

   
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
/**
 * This class provides static utility methods for buffered
 * copying between sources (<code>InputStream</code>, <code>Reader</code>,
 * <code>String</code> and <code>byte[]</code>) and destinations
 * (<code>OutputStream</code>, <code>Writer</code>, <code>String</code> and
 * <code>byte[]</code>).
 * <p>
 * Unless otherwise noted, these <code>copy</code> methods do <em>not</em>
 * flush or close the streams. Often doing so would require making non-portable
 * assumptions about the streams" origin and further use. This means that both
 * streams" <code>close()</code> methods must be called after copying. if one
 * omits this step, then the stream resources (sockets, file descriptors) are
 * released when the associated Stream is garbage-collected. It is not a good
 * idea to rely on this mechanism. For a good overview of the distinction
 * between "memory management" and "resource management", see
 *  for a list of valid encoding types.
     * @throws IOException In case of an I/O problem
     */
    public static void copy(
            InputStream input,
            Writer output,
            String encoding)
                throws IOException {
        InputStreamReader in = new InputStreamReader(input, encoding);
        copy(in, output);
    }

    // ----------------------------------------------------------------
    // Reader -> OutputStream
    // ----------------------------------------------------------------
    /**
     * Serialize chars from a <code>Reader</code> to bytes on an
     * <code>OutputStream</code>, and flush the <code>OutputStream</code>.
     * @param input the <code>Reader</code> to read from
     * @param output the <code>OutputStream</code> to write to
     * @throws IOException In case of an I/O problem
     */
    public static void copy(
            Reader input,
            OutputStream output)
                throws IOException {
        OutputStreamWriter out = new OutputStreamWriter(output);
        copy(input, out);
        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we
        // have to flush here.
        out.flush();
    }
    // ----------------------------------------------------------------
    // String -> OutputStream
    // ----------------------------------------------------------------
    /**
     * Serialize chars from a <code>String</code> to bytes on an
     * <code>OutputStream</code>, and
     * flush the <code>OutputStream</code>.
     * @param input the <code>String</code> to read from
     * @param output the <code>OutputStream</code> to write to
     * @throws IOException In case of an I/O problem
     */
    public static void copy(
            String input,
            OutputStream output)
                throws IOException {
        StringReader in = new StringReader(input);
        OutputStreamWriter out = new OutputStreamWriter(output);
        copy(in, out);
        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we
        // have to flush here.
        out.flush();
    }
    // ----------------------------------------------------------------
    // String -> Writer
    // ----------------------------------------------------------------
    /**
     * Copy chars from a <code>String</code> to a <code>Writer</code>.
     * @param input the <code>String</code> to read from
     * @param output the <code>Writer</code> to write to
     * @throws IOException In case of an I/O problem
     */
    public static void copy(String input, Writer output)
                throws IOException {
        output.write(input);
    }
}





Char Buffer

 
/*
 * Copyright (c) 1998 - 2005 Versant Corporation
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Versant Corporation - initial API and implementation
 */
/**
 * Fast StringBuffer replacement that allows direct access to the underlying
 * char[]. This is based com.sosnoski.util.array.CharArray from
 * Sosnoski Software Solutions, Inc.
 */
public final class CharBuf {
    private char[] buf;
    private int size;
    public CharBuf() {
        this(64);
    }
    public CharBuf(int capacity) {
        buf = new char[capacity];
    }
    public CharBuf(String s) {
        this(s.length());
        append(s);
    }
    public CharBuf(CharBuf s) {
        this(s.size);
        size = s.size;
        System.arraycopy(s.buf, 0, buf, 0, size);
    }
    public int size() {
        return size;
    }
    public void clear() {
        size = 0;
    }
    private void ensureCapacity(int len) {
        if (size + len > buf.length) {
            int n = buf.length * 3 / 2 + 1;
            if (size + len > n) {
                n = size + len;
            }
            char[] a = new char[n];
            System.arraycopy(buf, 0, a, 0, size);
            buf = a;
        }
    }
    /**
     * Append ch and return its index.
     */
    public int append(char ch) {
    ensureCapacity(size + 1);
    int start = size;
        buf[size++] = ch;
    return start;
  }
    /**
     * Append i and return its index.
     */
    public int append(int i) {
        return append(Integer.toString(i));
    }
    /**
     * Append s and return its index.
     */
    public int append(String s) {
        return append(s.toCharArray());
  }
    /**
     * Append a and return its index.
     */
    public int append(char[] a) {
        int n = a.length;
        ensureCapacity(size + n);
    int start = size;
        System.arraycopy(a, 0, buf, size, n);
        size += n;
    return start;
  }
    /**
     * Replace characters from i onwards with supplied characters. This does
     * not do any error checking or make any attempt to expand the buffer
     * for performance reasons.
     */
    public void replace(int i, char[] text) {
        System.arraycopy(text, 0, buf, i, text.length);
    }
    /**
     * Replace characters from first to last - 1 with c. This does
     * not do any error checking for performance reasons.
     */
    public void replace(int first, int last, char c) {
        for (int i = first; i < last; i++) {
            buf[i] = c;
        }
    }
    public String toString() {
        return new String(buf, 0, size);
    }
    /**
     * Constructs and returns a simple array containing the same data as held
     * in a portion of this growable array.
     */
    public char[] toArray(int offset, int length) {
        char[] a = new char[length];
        System.arraycopy(buf, offset, a, 0, length);
        return a;
    }
    public void setSize(int sz) {
        size = sz;
    }
    /**
     * Construct a <code>String</code> from a portion of the character sequence
     * present.
     */
    public String toString(int offset, int length) {
        return new String(buf, offset, length);
    }
    /**
     * Insert the characters from a <code>char[]</code> into the array.
     */
    public void insert(int offset, char[] text) {
        adjust(offset, offset, text.length);
        System.arraycopy(text, 0, buf, offset, text.length);
    }
    /**
     * Insert the characters from a <code>String</code> into the array.
     */
    public void insert(int offset, String text) {
        adjust(offset, offset, text.length());
        text.getChars(0, text.length(), buf, offset);
    }
    /**
     * Replace a character range in the array with the characters from a
     * <code>String</code>.
     */
    public void replace(int from, int to, String text) {
        adjust(from, to, text.length());
        text.getChars(0, text.length(), buf, from);
    }
    /**
     * Replace a character range in the array with the characters from a
     * <code>char[]</code>.
     */
    public void replace(int from, int to, char[] text) {
        adjust(from, to, text.length);
        System.arraycopy(text, 0, buf, from, text.length);
    }
    /**
     * Adjust the characters in the array to make room for an insertion or
     * replacement. Depending on the relative sizes of the range being
     * replaced and the range being inserted, this may move characters past
     * the start of the replacement range up or down in the array.
     *
     * @param from index number of first character to be replaced
     * @param to index number past last character to be replaced
     * @param length length of character range being inserted
     */
    protected void adjust(int from, int to, int length) {
        if (from >= 0 && to < size && from <= to) {
            int change = from + length - to;
            if (change > 0) {
                ensureCapacity(size + change);
            }
            if (to < size){
                System.arraycopy(buf, to, buf, to + change, size - to);
                size += change;
            }
        } else {
            throw new ArrayIndexOutOfBoundsException("Invalid remove range");
        }
    }
    /**
     * Set the value at an index position in the array.
     */
    public void set(int index, char value) {
        buf[index] = value;
    }
    /**
     * Remove a range of value from the array. The index positions for values
     * above the range removed are decreased by the number of values removed.
     *
     * @param from index number of first value to be removed
     * @param to index number past last value to be removed
     */
    public void remove(int from, int to) {
        if (from >= 0 && to <= size && from <= to) {
            if (to < size){
                int change = from - to;
                System.arraycopy(buf, to, buf, from, size - to);
                size += change;
            }
        } else {
            throw new ArrayIndexOutOfBoundsException("Invalid remove range");
        }
    }
}





Circular Byte Buffer

   
/*
 * Circular Byte Buffer
 * Copyright (C) 2002 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * See COPYING.TXT for details.
 */
import java.io.*;
/**
 * Implements the Circular Buffer producer/consumer model for bytes.
 * More information about this class is available from .
 * <p>
 * Using this class is a simpler alternative to using a PipedInputStream
 * and a PipedOutputStream. PipedInputStreams and PipedOutputStreams don"t support the
 * mark operation, don"t allow you to control buffer sizes that they use,
 * and have a more complicated API that requires instantiating two
 * classes and connecting them.
 * <p>
 * This class is thread safe.
 *
 * @see CircularCharBuffer
 * @see CircularObjectBuffer
 *
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.00.00
 */
public class CircularByteBuffer {
  /**
   * The default size for a circular byte buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  private final static int DEFAULT_SIZE = 1024;
  /**
   * A buffer that will grow as things are added.
   *
   * @since ostermillerutils 1.00.00
   */
  public final static int INFINITE_SIZE = -1;
  /**
   * The circular buffer.
   * <p>
   * The actual capacity of the buffer is one less than the actual length
   * of the buffer so that an empty and a full buffer can be
   * distinguished.  An empty buffer will have the markPostion and the
   * writePosition equal to each other.  A full buffer will have
   * the writePosition one less than the markPostion.
   * <p>
   * There are three important indexes into the buffer:
   * The readPosition, the writePosition, and the markPosition.
   * If the InputStream has never been marked, the readPosition and
   * the markPosition should always be the same.  The bytes
   * available to be read go from the readPosition to the writePosition,
   * wrapping around the end of the buffer.  The space available for writing
   * goes from the write position to one less than the markPosition,
   * wrapping around the end of the buffer.  The bytes that have
   * been saved to support a reset() of the InputStream go from markPosition
   * to readPosition, wrapping around the end of the buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected byte[] buffer;
  /**
   * Index of the first byte available to be read.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int readPosition = 0;
  /**
   * Index of the first byte available to be written.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int writePosition = 0;
  /**
   * Index of the first saved byte. (To support stream marking.)
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int markPosition = 0;
  /**
   * Number of bytes that have to be saved
   * to support mark() and reset() on the InputStream.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int markSize = 0;
  /**
   * If this buffer is infinite (should resize itself when full)
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile boolean infinite = false;
  /**
   * True if a write to a full buffer should block until the buffer
   * has room, false if the write method should throw an IOException
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean blockingWrite = true;
  /**
   * The InputStream that can empty this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected InputStream in = new CircularByteBufferInputStream();
  /**
   * true if the close() method has been called on the InputStream
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean inputStreamClosed = false;
  /**
   * The OutputStream that can fill this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected OutputStream out = new CircularByteBufferOutputStream();
  /**
   * true if the close() method has been called on the OutputStream
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean outputStreamClosed = false;
  /**
   * Make this buffer ready for reuse.  The contents of the buffer
   * will be cleared and the streams associated with this buffer
   * will be reopened if they had been closed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void clear(){
    synchronized (this){
      readPosition = 0;
      writePosition = 0;
      markPosition = 0;
      outputStreamClosed = false;
      inputStreamClosed = false;
    }
  }
  /**
   * Retrieve a OutputStream that can be used to fill
   * this buffer.
   * <p>
   * Write methods may throw a BufferOverflowException if
   * the buffer is not large enough.  A large enough buffer
   * size must be chosen so that this does not happen or
   * the caller must be prepared to catch the exception and
   * try again once part of the buffer has been consumed.
   *
   *
   * @return the producer for this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  public OutputStream getOutputStream(){
    return out;
  }
  /**
   * Retrieve a InputStream that can be used to empty
   * this buffer.
   * <p>
   * This InputStream supports marks at the expense
   * of the buffer size.
   *
   * @return the consumer for this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  public InputStream getInputStream(){
    return in;
  }
  /**
   * Get number of bytes that are available to be read.
   * <p>
   * Note that the number of bytes available plus
   * the number of bytes free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the size in bytes of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getAvailable(){
    synchronized (this){
      return available();
    }
  }
  /**
   * Get the number of bytes this buffer has free for
   * writing.
   * <p>
   * Note that the number of bytes available plus
   * the number of bytes free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the available space in bytes of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getSpaceLeft(){
    synchronized (this){
      return spaceLeft();
    }
  }
  /**
   * Get the capacity of this buffer.
   * <p>
   * Note that the number of bytes available plus
   * the number of bytes free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the size in bytes of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getSize(){
    synchronized (this){
      return buffer.length;
    }
  }
  /**
   * double the size of the buffer
   *
   * @since ostermillerutils 1.00.00
   */
  private void resize(){
    byte[] newBuffer = new byte[buffer.length * 2];
    int marked = marked();
    int available = available();
    if (markPosition <= writePosition){
      // any space between the mark and
      // the first write needs to be saved.
      // In this case it is all in one piece.
      int length = writePosition - markPosition;
      System.arraycopy(buffer, markPosition, newBuffer, 0, length);
    } else {
      int length1 = buffer.length - markPosition;
      System.arraycopy(buffer, markPosition, newBuffer, 0, length1);
      int length2 = writePosition;
      System.arraycopy(buffer, 0, newBuffer, length1, length2);
    }
    buffer = newBuffer;
    markPosition = 0;
    readPosition = marked;
    writePosition = marked + available;
  }
  /**
   * Space available in the buffer which can be written.
   *
   * @since ostermillerutils 1.00.00
   */
  private int spaceLeft(){
    if (writePosition < markPosition){
      // any space between the first write and
      // the mark except one byte is available.
      // In this case it is all in one piece.
      return (markPosition - writePosition - 1);
    }
    // space at the beginning and end.
    return ((buffer.length - 1) - (writePosition - markPosition));
  }
  /**
   * Bytes available for reading.
   *
   * @since ostermillerutils 1.00.00
   */
  private int available(){
    if (readPosition <= writePosition){
      // any space between the first read and
      // the first write is available.  In this case i
      // is all in one piece.
      return (writePosition - readPosition);
    }
    // space at the beginning and end.
    return (buffer.length - (readPosition - writePosition));
  }
  /**
   * Bytes saved for supporting marks.
   *
   * @since ostermillerutils 1.00.00
   */
  private int marked(){
    if (markPosition <= readPosition){
      // any space between the markPosition and
      // the first write is marked.  In this case i
      // is all in one piece.
      return (readPosition - markPosition);
    }
    // space at the beginning and end.
    return (buffer.length - (markPosition - readPosition));
  }
  /**
   * If we have passed the markSize reset the
   * mark so that the space can be used.
   *
   * @since ostermillerutils 1.00.00
   */
  private void ensureMark(){
    if (marked() >= markSize){
      markPosition = readPosition;
      markSize = 0;
    }
  }
  /**
   * Create a new buffer with a default capacity.
   * Writing to a full buffer will block until space
   * is available rather than throw an exception.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularByteBuffer(){
    this (DEFAULT_SIZE, true);
  }
  /**
   * Create a new buffer with given capacity.
   * Writing to a full buffer will block until space
   * is available rather than throw an exception.
   * <p>
   * Note that the buffer may reserve some bytes for
   * special purposes and capacity number of bytes may
   * not be able to be written to the buffer.
   * <p>
   * Note that if the buffer is of INFINITE_SIZE it will
   * neither block or throw exceptions, but rather grow
   * without bound.
   *
   * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularByteBuffer(int size){
    this (size, true);
  }
  /**
   * Create a new buffer with a default capacity and
   * given blocking behavior.
   *
   * @param blockingWrite true writing to a full buffer should block
   *        until space is available, false if an exception should
   *        be thrown instead.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularByteBuffer(boolean blockingWrite){
    this (DEFAULT_SIZE, blockingWrite);
  }
  /**
   * Create a new buffer with the given capacity and
   * blocking behavior.
   * <p>
   * Note that the buffer may reserve some bytes for
   * special purposes and capacity number of bytes may
   * not be able to be written to the buffer.
   * <p>
   * Note that if the buffer is of INFINITE_SIZE it will
   * neither block or throw exceptions, but rather grow
   * without bound.
   *
   * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE.
   * @param blockingWrite true writing to a full buffer should block
   *        until space is available, false if an exception should
   *        be thrown instead.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularByteBuffer(int size, boolean blockingWrite){
    if (size == INFINITE_SIZE){
      buffer = new byte[DEFAULT_SIZE];
      infinite = true;
    } else {
      buffer = new byte[size];
      infinite = false;
    }
    this.blockingWrite = blockingWrite;
  }
  /**
   * Class for reading from a circular byte buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected class CircularByteBufferInputStream extends InputStream {
    /**
     * Returns the number of bytes that can be read (or skipped over) from this
     * input stream without blocking by the next caller of a method for this input
     * stream. The next caller might be the same thread or or another thread.
     *
     * @return the number of bytes that can be read from this input stream without blocking.
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public int available() throws IOException {
      synchronized (CircularByteBuffer.this){
        if (inputStreamClosed) throw new IOException("InputStream has been closed, it is not ready.");
        return (CircularByteBuffer.this.available());
      }
    }
    /**
     * Close the stream. Once a stream has been closed, further read(), available(),
     * mark(), or reset() invocations will throw an IOException. Closing a
     * previously-closed stream, however, has no effect.
     *
     * @throws IOException never.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void close() throws IOException {
      synchronized (CircularByteBuffer.this){
        inputStreamClosed = true;
      }
    }
    /**
     * Mark the present position in the stream. Subsequent calls to reset() will
     * attempt to reposition the stream to this point.
     * <p>
     * The readAheadLimit must be less than the size of circular buffer, otherwise
     * this method has no effect.
     *
     * @param readAheadLimit Limit on the number of bytes that may be read while
     *    still preserving the mark. After reading this many bytes, attempting to
     *    reset the stream will fail.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void mark(int readAheadLimit) {
      synchronized (CircularByteBuffer.this){
        //if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot mark a closed InputStream.");
        if (buffer.length - 1 > readAheadLimit) {
          markSize = readAheadLimit;
          markPosition = readPosition;
        }
      }
    }
    /**
     * Tell whether this stream supports the mark() operation.
     *
     * @return true, mark is supported.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public boolean markSupported() {
      return true;
    }
    /**
     * Read a single byte.
     * This method will block until a byte is available, an I/O error occurs,
     * or the end of the stream is reached.
     *
     * @return The byte read, as an integer in the range 0 to 255 (0x00-0xff),
     *     or -1 if the end of the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public int read() throws IOException {
      while (true){
        synchronized (CircularByteBuffer.this){
          if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot read from a closed InputStream.");
          int available = CircularByteBuffer.this.available();
          if (available > 0){
            int result = buffer[readPosition] & 0xff;
            readPosition++;
            if (readPosition == buffer.length){
              readPosition = 0;
            }
            ensureMark();
            return result;
          } else if (outputStreamClosed){
            return -1;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
    /**
     * Read bytes into an array.
     * This method will block until some input is available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param cbuf Destination buffer.
     * @return The number of bytes read, or -1 if the end of
     *   the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public int read(byte[] cbuf) throws IOException {
      return read(cbuf, 0, cbuf.length);
    }
    /**
     * Read bytes into a portion of an array.
     * This method will block until some input is available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param cbuf Destination buffer.
     * @param off Offset at which to start storing bytes.
     * @param len Maximum number of bytes to read.
     * @return The number of bytes read, or -1 if the end of
     *   the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public int read(byte[] cbuf, int off, int len) throws IOException {
      while (true){
        synchronized (CircularByteBuffer.this){
          if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot read from a closed InputStream.");
          int available = CircularByteBuffer.this.available();
          if (available > 0){
            int length = Math.min(len, available);
            int firstLen = Math.min(length, buffer.length - readPosition);
            int secondLen = length - firstLen;
            System.arraycopy(buffer, readPosition, cbuf, off, firstLen);
            if (secondLen > 0){
              System.arraycopy(buffer, 0, cbuf, off+firstLen,  secondLen);
              readPosition = secondLen;
            } else {
              readPosition += length;
            }
            if (readPosition == buffer.length) {
              readPosition = 0;
            }
            ensureMark();
            return length;
          } else if (outputStreamClosed){
            return -1;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
    /**
     * Reset the stream.
     * If the stream has been marked, then attempt to reposition i
     * at the mark. If the stream has not been marked, or more bytes
     * than the readAheadLimit have been read, this method has no effect.
     *
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void reset() throws IOException {
      synchronized (CircularByteBuffer.this){
        if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot reset a closed InputStream.");
        readPosition = markPosition;
      }
    }
    /**
     * Skip bytes.
     * This method will block until some bytes are available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param n The number of bytes to skip
     * @return The number of bytes actually skipped
     * @throws IllegalArgumentException if n is negative.
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public long skip(long n) throws IOException, IllegalArgumentException {
      while (true){
        synchronized (CircularByteBuffer.this){
          if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot skip bytes on a closed InputStream.");
          int available = CircularByteBuffer.this.available();
          if (available > 0){
            int length = Math.min((int)n, available);
            int firstLen = Math.min(length, buffer.length - readPosition);
            int secondLen = length - firstLen;
            if (secondLen > 0){
              readPosition = secondLen;
            } else {
              readPosition += length;
            }
            if (readPosition == buffer.length) {
              readPosition = 0;
            }
            ensureMark();
            return length;
          } else if (outputStreamClosed){
            return 0;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
  }
  /**
   * Class for writing to a circular byte buffer.
   * If the buffer is full, the writes will either block
   * until there is some space available or throw an IOException
   * based on the CircularByteBuffer"s preference.
   *
   * @since ostermillerutils 1.00.00
   */
  protected class CircularByteBufferOutputStream extends OutputStream {
    /**
     * Close the stream, flushing it first.
     * This will cause the InputStream associated with this circular buffer
     * to read its last bytes once it empties the buffer.
     * Once a stream has been closed, further write() or flush() invocations
     * will cause an IOException to be thrown. Closing a previously-closed stream,
     * however, has no effect.
     *
     * @throws IOException never.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void close() throws IOException {
      synchronized (CircularByteBuffer.this){
        if (!outputStreamClosed){
          flush();
        }
        outputStreamClosed = true;
      }
    }
    /**
     * Flush the stream.
     *
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void flush() throws IOException {
      if (outputStreamClosed) throw new IOException("OutputStream has been closed; cannot flush a closed OutputStream.");
      if (inputStreamClosed) throw new IOException("Buffer closed by inputStream; cannot flush.");
      // this method needs to do nothing
    }
    /**
     * Write an array of bytes.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param cbuf Array of bytes to be written
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(byte[] cbuf) throws IOException {
      write(cbuf, 0, cbuf.length);
    }
    /**
     * Write a portion of an array of bytes.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param cbuf Array of bytes
     * @param off Offset from which to start writing bytes
     * @param len - Number of bytes to write
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(byte[] cbuf, int off, int len) throws IOException {
      while (len > 0){
        synchronized (CircularByteBuffer.this){
          if (outputStreamClosed) throw new IOException("OutputStream has been closed; cannot write to a closed OutputStream.");
          if (inputStreamClosed) throw new IOException("Buffer closed by InputStream; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < len){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < len) throw new Exception("CircularByteBuffer is full; cannot write " + len + " bytes");
          int realLen = Math.min(len, spaceLeft);
          int firstLen = Math.min(realLen, buffer.length - writePosition);
          int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1);
          int written = firstLen + secondLen;
          if (firstLen > 0){
            System.arraycopy(cbuf, off, buffer, writePosition, firstLen);
          }
          if (secondLen > 0){
            System.arraycopy(cbuf, off+firstLen, buffer, 0, secondLen);
            writePosition = secondLen;
          } else {
            writePosition += written;
          }
          if (writePosition == buffer.length) {
            writePosition = 0;
          }
          off += written;
          len -= written;
        }
        if (len > 0){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
    /**
     * Write a single byte.
     * The byte to be written is contained in the 8 low-order bits of the
     * given integer value; the 24 high-order bits are ignored.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param c number of bytes to be written
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(int c) throws IOException {
      boolean written = false;
      while (!written){
        synchronized (CircularByteBuffer.this){
          if (outputStreamClosed) throw new IOException("OutputStream has been closed; cannot write to a closed OutputStream.");
          if (inputStreamClosed) throw new IOException("Buffer closed by InputStream; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < 1){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < 1) throw new Exception("CircularByteBuffer is full; cannot write 1 byte");
          if (spaceLeft > 0){
            buffer[writePosition] = (byte)(c & 0xff);
            writePosition++;
            if (writePosition == buffer.length) {
              writePosition = 0;
            }
            written = true;
          }
        }
        if (!written){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
  }
}





Circular Char Buffer

  
/*
 * Circular Character Buffer
 * Copyright (C) 2002 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * See COPYING.TXT for details.
 */
import java.io.*;
/**
 * Implements the Circular Buffer producer/consumer model for characters.
 * More information about this class is available from .
 * <p>
 * Using this class is a simpler alternative to using a PipedReader
 * and a PipedWriter. PipedReaders and PipedWriters don"t support the
 * mark operation, don"t allow you to control buffer sizes that they use,
 * and have a more complicated API that requires instantiating two
 * classes and connecting them.
 * <p>
 * This class is thread safe.
 *
 * @see CircularByteBuffer
 * @see CircularObjectBuffer
 *
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.00.00
 */
public class CircularCharBuffer {
  /**
   * The default size for a circular character buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  private final static int DEFAULT_SIZE = 1024;
  /**
   * A buffer that will grow as things are added.
   *
   * @since ostermillerutils 1.00.00
   */
  public final static int INFINITE_SIZE = -1;
  /**
   * The circular buffer.
   * <p>
   * The actual capacity of the buffer is one less than the actual length
   * of the buffer so that an empty and a full buffer can be
   * distinguished.  An empty buffer will have the markPostion and the
   * writePosition equal to each other.  A full buffer will have
   * the writePosition one less than the markPostion.
   * <p>
   * There are three important indexes into the buffer:
   * The readPosition, the writePosition, and the markPosition.
   * If the Reader has never been marked, the readPosition and
   * the markPosition should always be the same.  The characters
   * available to be read go from the readPosition to the writePosition,
   * wrapping around the end of the buffer.  The space available for writing
   * goes from the write position to one less than the markPosition,
   * wrapping around the end of the buffer.  The characters that have
   * been saved to support a reset() of the Reader go from markPosition
   * to readPosition, wrapping around the end of the buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected char[] buffer;
  /**
   * Index of the first character available to be read.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int readPosition = 0;
  /**
   * Index of the first character available to be written.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int writePosition = 0;
  /**
   * Index of the first saved character. (To support stream marking.)
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int markPosition = 0;
  /**
   * Number of characters that have to be saved
   * to support mark() and reset() on the Reader.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int markSize = 0;
  /**
   * If this buffer is infinite (should resize itself when full)
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile boolean infinite = false;
  /**
   * True if a write to a full buffer should block until the buffer
   * has room, false if the write method should throw an IOException
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean blockingWrite = true;
  /**
   * The Reader that can empty this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected Reader reader = new CircularCharBufferReader();
  /**
   * true if the close() method has been called on the Reader
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean readerClosed = false;
  /**
   * The Writer that can fill this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected Writer writer = new CircularCharBufferWriter();
  /**
   * true if the close() method has been called on the writer
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean writerClosed = false;
  /**
   * Make this buffer ready for reuse.  The contents of the buffer
   * will be cleared and the streams associated with this buffer
   * will be reopened if they had been closed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void clear(){
    synchronized (this){
      readPosition = 0;
      writePosition = 0;
      markPosition = 0;
      readerClosed = false;
      writerClosed = false;
    }
  }
  /**
   * Retrieve a Writer that can be used to fill
   * this buffer.
   * <p>
   * Write methods may throw a BufferOverflowException if
   * the buffer is not large enough.  A large enough buffer
   * size must be chosen so that this does not happen or
   * the caller must be prepared to catch the exception and
   * try again once part of the buffer has been consumed.
   *
   *
   * @return the producer for this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  public Writer getWriter(){
    return writer;
  }
  /**
   * Retrieve a Reader that can be used to empty
   * this buffer.
   * <p>
   * This Reader supports marks at the expense
   * of the buffer size.
   *
   * @return the consumer for this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  public Reader getReader(){
    return reader;
  }
  /**
   * Get number of characters that are available to be read.
   * <p>
   * Note that the number of characters available plus
   * the number of characters free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the size in characters of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getAvailable(){
    synchronized (this){
      return available();
    }
  }
  /**
   * Get the number of characters this buffer has free for
   * writing.
   * <p>
   * Note that the number of characters available plus
   * the number of characters free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the available space in characters of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getSpaceLeft(){
    synchronized (this){
      return spaceLeft();
    }
  }
  /**
   * Get the capacity of this buffer.
   * <p>
   * Note that the number of characters available plus
   * the number of characters free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the size in characters of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getSize(){
    synchronized (this){
      return buffer.length;
    }
  }
  /**
   * double the size of the buffer
   *
   * @since ostermillerutils 1.00.00
   */
  private void resize(){
    char[] newBuffer = new char[buffer.length * 2];
    int marked = marked();
    int available = available();
    if (markPosition <= writePosition){
      // any space between the mark and
      // the first write needs to be saved.
      // In this case it is all in one piece.
      int length = writePosition - markPosition;
      System.arraycopy(buffer, markPosition, newBuffer, 0, length);
    } else {
      int length1 = buffer.length - markPosition;
      System.arraycopy(buffer, markPosition, newBuffer, 0, length1);
      int length2 = writePosition;
      System.arraycopy(buffer, 0, newBuffer, length1, length2);
    }
    buffer = newBuffer;
    markPosition = 0;
    readPosition = marked;
    writePosition = marked + available;
  }
  /**
   * Space available in the buffer which can be written.
   *
   * @since ostermillerutils 1.00.00
   */
  private int spaceLeft(){
    if (writePosition < markPosition){
      // any space between the first write and
      // the mark except one character is available.
      // In this case it is all in one piece.
      return (markPosition - writePosition - 1);
    }
    // space at the beginning and end.
    return ((buffer.length - 1) - (writePosition - markPosition));
  }
  /**
   * Characters available for reading.
   *
   * @since ostermillerutils 1.00.00
   */
  private int available(){
    if (readPosition <= writePosition){
      // any space between the first read and
      // the first write is available.  In this case i
      // is all in one piece.
      return (writePosition - readPosition);
    }
    // space at the beginning and end.
    return (buffer.length - (readPosition - writePosition));
  }
  /**
   * Characters saved for supporting marks.
   *
   * @since ostermillerutils 1.00.00
   */
  private int marked(){
    if (markPosition <= readPosition){
      // any space between the markPosition and
      // the first write is marked.  In this case i
      // is all in one piece.
      return (readPosition - markPosition);
    }
    // space at the beginning and end.
    return (buffer.length - (markPosition - readPosition));
  }
  /**
   * If we have passed the markSize reset the
   * mark so that the space can be used.
   *
   * @since ostermillerutils 1.00.00
   */
  private void ensureMark(){
    if (marked() >= markSize){
      markPosition = readPosition;
      markSize = 0;
    }
  }
  /**
   * Create a new buffer with a default capacity.
   * Writing to a full buffer will block until space
   * is available rather than throw an exception.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(){
    this (DEFAULT_SIZE, true);
  }
  /**
   * Create a new buffer with given capacity.
   * Writing to a full buffer will block until space
   * is available rather than throw an exception.
   * <p>
   * Note that the buffer may reserve some characters for
   * special purposes and capacity number of characters may
   * not be able to be written to the buffer.
   * <p>
   * Note that if the buffer is of INFINITE_SIZE it will
   * neither block or throw exceptions, but rather grow
   * without bound.
   *
   * @param size desired capacity of the buffer in characters or CircularCharBuffer.INFINITE_SIZE
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(int size){
    this (size, true);
  }
  /**
   * Create a new buffer with a default capacity and
   * given blocking behavior.
   *
   * @param blockingWrite true writing to a full buffer should block
   *        until space is available, false if an exception should
   *        be thrown instead.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(boolean blockingWrite){
    this (DEFAULT_SIZE, blockingWrite);
  }
  /**
   * Create a new buffer with the given capacity and
   * blocking behavior.
   * <p>
   * Note that the buffer may reserve some characters for
   * special purposes and capacity number of characters may
   * not be able to be written to the buffer.
   * <p>
   * Note that if the buffer is of CircularCharBuffer.INFINITE_SIZE it will
   * neither block or throw exceptions, but rather grow
   * without bound.
   *
   * @param size desired capacity of the buffer in characters or CircularCharBuffer.INFINITE_SIZE
   * @param blockingWrite true writing to a full buffer should block
   *        until space is available, false if an exception should
   *        be thrown instead.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(int size, boolean blockingWrite){
    if (size == INFINITE_SIZE){
      buffer = new char[DEFAULT_SIZE];
      infinite = true;
    } else {
      buffer = new char[size];
      infinite = false;
    }
    this.blockingWrite = blockingWrite;
  }
  /**
   * Class for reading from a circular character buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected class CircularCharBufferReader extends Reader {
    /**
     * Close the stream. Once a stream has been closed, further read(), ready(),
     * mark(), or reset() invocations will throw an IOException. Closing a
     * previously-closed stream, however, has no effect.
     *
     * @throws IOException never.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void close() throws IOException {
      synchronized (CircularCharBuffer.this){
        readerClosed = true;
      }
    }
    /**
     * Mark the present position in the stream. Subsequent calls to reset() will
     * attempt to reposition the stream to this point.
     * <p>
     * The readAheadLimit must be less than the size of circular buffer.
     *
     * @param readAheadLimit Limit on the number of characters that may be read while
     *    still preserving the mark. After reading this many characters, attempting to
     *    reset the stream will fail.
     * @throws IOException if the stream is closed, or the buffer size is greater
     * than or equal to the readAheadLimit.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void mark(int readAheadLimit) throws IOException {
      synchronized (CircularCharBuffer.this){
        if (readerClosed) throw new IOException("Reader has been closed; cannot mark a closed Reader.");
        if (buffer.length - 1 <= readAheadLimit) throw new IOException("Cannot mark stream, readAheadLimit bigger than buffer size.");
        markSize = readAheadLimit;
        markPosition = readPosition;
      }
    }
    /**
     * Tell whether this stream supports the mark() operation.
     *
     * @return true, mark is supported.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public boolean markSupported() {
      return true;
    }
    /**
     * Read a single character.
     * This method will block until a character is available, an I/O error occurs,
     * or the end of the stream is reached.
     *
     * @return The character read, as an integer in the range 0 to 65535 (0x00-0xffff),
     *     or -1 if the end of the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public int read() throws IOException {
      while (true){
        synchronized (CircularCharBuffer.this){
          if (readerClosed) throw new IOException("Reader has been closed; cannot read from a closed Reader.");
          int available = available();
          if (available > 0){
            int result = buffer[readPosition] & 0xffff;
            readPosition++;
            if (readPosition == buffer.length){
              readPosition = 0;
            }
            ensureMark();
            return result;
          } else if (writerClosed){
            return -1;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
    /**
     * Read characters into an array.
     * This method will block until some input is available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param cbuf Destination buffer.
     * @return The number of characters read, or -1 if the end of
     *   the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public int read(char[] cbuf) throws IOException {
      return read(cbuf, 0, cbuf.length);
    }
    /**
     * Read characters into a portion of an array.
     * This method will block until some input is available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param cbuf Destination buffer.
     * @param off Offset at which to start storing characters.
     * @param len Maximum number of characters to read.
     * @return The number of characters read, or -1 if the end of
     *   the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public int read(char[] cbuf, int off, int len) throws IOException {
      while (true){
        synchronized (CircularCharBuffer.this){
          if (readerClosed) throw new IOException("Reader has been closed; cannot read from a closed Reader.");
          int available = available();
          if (available > 0){
            int length = Math.min(len, available);
            int firstLen = Math.min(length, buffer.length - readPosition);
            int secondLen = length - firstLen;
            System.arraycopy(buffer, readPosition, cbuf, off, firstLen);
            if (secondLen > 0){
              System.arraycopy(buffer, 0, cbuf, off+firstLen,  secondLen);
              readPosition = secondLen;
            } else {
              readPosition += length;
            }
            if (readPosition == buffer.length) {
              readPosition = 0;
            }
            ensureMark();
            return length;
          } else if (writerClosed){
            return -1;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
    /**
     * Tell whether this stream is ready to be read.
     *
     * @return True if the next read() is guaranteed not to block for input,
     *    false otherwise. Note that returning false does not guarantee that
     *    the next read will block.
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public boolean ready() throws IOException {
      synchronized (CircularCharBuffer.this){
        if (readerClosed) throw new IOException("Reader has been closed, it is not ready.");
        return (available() > 0);
      }
    }
    /**
     * Reset the stream.
     * If the stream has been marked, then attempt to reposition i
     * at the mark. If the stream has not been marked, or more characters
     * than the readAheadLimit have been read, this method has no effect.
     *
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void reset() throws IOException {
      synchronized (CircularCharBuffer.this){
        if (readerClosed) throw new IOException("Reader has been closed; cannot reset a closed Reader.");
        readPosition = markPosition;
      }
    }
    /**
     * Skip characters.
     * This method will block until some characters are available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param n The number of characters to skip
     * @return The number of characters actually skipped
     * @throws IllegalArgumentException if n is negative.
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public long skip(long n) throws IOException, IllegalArgumentException {
      while (true){
        synchronized (CircularCharBuffer.this){
          if (readerClosed) throw new IOException("Reader has been closed; cannot skip characters on a closed Reader.");
          int available = available();
          if (available > 0){
            int length = Math.min((int)n, available);
            int firstLen = Math.min(length, buffer.length - readPosition);
            int secondLen = length - firstLen;
            if (secondLen > 0){
              readPosition = secondLen;
            } else {
              readPosition += length;
            }
            if (readPosition == buffer.length) {
              readPosition = 0;
            }
            ensureMark();
            return length;
          } else if (writerClosed){
            return 0;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
  }
  /**
   * Class for writing to a circular character buffer.
   * If the buffer is full, the writes will either block
   * until there is some space available or throw an IOException
   * based on the CircularCharBuffer"s preference.
   *
   * @since ostermillerutils 1.00.00
   */
  protected class CircularCharBufferWriter extends Writer {
    /**
     * Close the stream, flushing it first.
     * This will cause the reader associated with this circular buffer
     * to read its last characters once it empties the buffer.
     * Once a stream has been closed, further write() or flush() invocations
     * will cause an IOException to be thrown. Closing a previously-closed stream,
     * however, has no effect.
     *
     * @throws IOException never.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void close() throws IOException {
      synchronized (CircularCharBuffer.this){
        if (!writerClosed){
          flush();
        }
        writerClosed = true;
      }
    }
    /**
     * Flush the stream.
     *
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void flush() throws IOException {
      if (writerClosed) throw new IOException("Writer has been closed; cannot flush a closed Writer.");
      if (readerClosed) throw new IOException("Buffer closed by Reader; cannot flush.");
      // this method needs to do nothing
    }
    /**
     * Write an array of characters.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param cbuf Array of characters to be written
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(char[] cbuf) throws IOException {
      write(cbuf, 0, cbuf.length);
    }
    /**
     * Write a portion of an array of characters.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param cbuf Array of characters
     * @param off Offset from which to start writing characters
     * @param len - Number of characters to write
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(char[] cbuf, int off, int len) throws IOException {
      while (len > 0){
        synchronized (CircularCharBuffer.this){
          if (writerClosed) throw new IOException("Writer has been closed; cannot write to a closed Writer.");
          if (readerClosed) throw new IOException("Buffer closed by Reader; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < len){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < len) throw new Exception("CircularCharBuffer is full; cannot write " + len + " characters");
          int realLen = Math.min(len, spaceLeft);
          int firstLen = Math.min(realLen, buffer.length - writePosition);
          int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1);
          int written = firstLen + secondLen;
          if (firstLen > 0){
            System.arraycopy(cbuf, off, buffer, writePosition, firstLen);
          }
          if (secondLen > 0){
            System.arraycopy(cbuf, off+firstLen, buffer, 0, secondLen);
            writePosition = secondLen;
          } else {
            writePosition += written;
          }
          if (writePosition == buffer.length) {
            writePosition = 0;
          }
          off += written;
          len -= written;
        }
        if (len > 0){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
    /**
     * Write a single character.
     * The character to be written is contained in the 16 low-order bits of the
     * given integer value; the 16 high-order bits are ignored.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param c number of characters to be written
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(int c) throws IOException {
      boolean written = false;
      while (!written){
        synchronized (CircularCharBuffer.this){
          if (writerClosed) throw new IOException("Writer has been closed; cannot write to a closed Writer.");
          if (readerClosed) throw new IOException("Buffer closed by Reader; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < 1){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < 1) throw new Exception("CircularCharBuffer is full; cannot write 1 character");
          if (spaceLeft > 0){
            buffer[writePosition] = (char)(c & 0xffff);
            writePosition++;
            if (writePosition == buffer.length) {
              writePosition = 0;
            }
            written = true;
          }
        }
        if (!written){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
    /**
     * Write a string.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param str String to be written
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(String str) throws IOException {
      write(str, 0, str.length());
    }
    /**
     * Write a portion of a string.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param str A String
     * @param off Offset from which to start writing characters
     * @param len Number of characters to write
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    @Override public void write(String str, int off, int len) throws IOException {
      while (len > 0){
        synchronized (CircularCharBuffer.this){
          if (writerClosed) throw new IOException("Writer has been closed; cannot write to a closed Writer.");
          if (readerClosed) throw new IOException("Buffer closed by Reader; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < len){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < len) throw new Exception("CircularCharBuffer is full; cannot write " + len + " characters");
          int realLen = Math.min(len, spaceLeft);
          int firstLen = Math.min(realLen, buffer.length - writePosition);
          int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1);
          int written = firstLen + secondLen;
          for (int i=0; i<firstLen; i++){
            buffer[writePosition + i] = str.charAt(off+i);
          }
          if (secondLen > 0){
            for (int i=0; i<secondLen; i++){
              buffer[i] = str.charAt(off+firstLen+i);
            }
            writePosition = secondLen;
          } else {
            writePosition += written;
          }
          if (writePosition == buffer.length) {
            writePosition = 0;
          }
          off += written;
          len -= written;
        }
        if (len > 0){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
  }
}





Circular Char Buffer from http://ostermiller.org

   
/*
 * Circular Character Buffer
 * Copyright (C) 2002 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * See COPYING.TXT for details.
 */
import java.io.Reader;
import java.io.Writer;
import java.io.IOException;
/**
 * Implements the Circular Buffer producer/consumer model for characters.
 * More information about this class is available from .
 * <p>
 * Using this class is a simpler alternative to using a PipedReader
 * and a PipedWriter. PipedReaders and PipedWriters don"t support the
 * mark operation, don"t allow you to control buffer sizes that they use,
 * and have a more complicated API that requires instantiating two
 * classes and connecting them.
 * <p>
 * This class is thread safe.
 *
 * @see CircularByteBuffer
 * @see CircularObjectBuffer
 *
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.00.00
 */
public class CircularCharBuffer {
  /**
   * The default size for a circular character buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  private final static int DEFAULT_SIZE = 1024;
  /**
   * A buffer that will grow as things are added.
   *
   * @since ostermillerutils 1.00.00
   */
  public final static int INFINITE_SIZE = -1;
  /**
   * The circular buffer.
   * <p>
   * The actual capacity of the buffer is one less than the actual length
   * of the buffer so that an empty and a full buffer can be
   * distinguished.  An empty buffer will have the markPostion and the
   * writePosition equal to each other.  A full buffer will have
   * the writePosition one less than the markPostion.
   * <p>
   * There are three important indexes into the buffer:
   * The readPosition, the writePosition, and the markPosition.
   * If the Reader has never been marked, the readPosition and
   * the markPosition should always be the same.  The characters
   * available to be read go from the readPosition to the writePosition,
   * wrapping around the end of the buffer.  The space available for writing
   * goes from the write position to one less than the markPosition,
   * wrapping around the end of the buffer.  The characters that have
   * been saved to support a reset() of the Reader go from markPosition
   * to readPosition, wrapping around the end of the buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected char[] buffer;
  /**
   * Index of the first character available to be read.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int readPosition = 0;
  /**
   * Index of the first character available to be written.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int writePosition = 0;
  /**
   * Index of the first saved character. (To support stream marking.)
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int markPosition = 0;
  /**
   * Number of characters that have to be saved
   * to support mark() and reset() on the Reader.
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile int markSize = 0;
  /**
   * If this buffer is infinite (should resize itself when full)
   *
   * @since ostermillerutils 1.00.00
   */
  protected volatile boolean infinite = false;
  /**
   * True if a write to a full buffer should block until the buffer
   * has room, false if the write method should throw an IOException
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean blockingWrite = true;
  /**
   * The Reader that can empty this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected Reader reader = new CircularCharBufferReader();
  /**
   * true if the close() method has been called on the Reader
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean readerClosed = false;
  /**
   * The Writer that can fill this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected Writer writer = new CircularCharBufferWriter();
  /**
   * true if the close() method has been called on the writer
   *
   * @since ostermillerutils 1.00.00
   */
  protected boolean writerClosed = false;
  /**
   * Make this buffer ready for reuse.  The contents of the buffer
   * will be cleared and the streams associated with this buffer
   * will be reopened if they had been closed.
   *
   * @since ostermillerutils 1.00.00
   */
  public void clear(){
    synchronized (this){
      readPosition = 0;
      writePosition = 0;
      markPosition = 0;
      readerClosed = false;
      writerClosed = false;
    }
  }
  /**
   * Retrieve a Writer that can be used to fill
   * this buffer.
   * <p>
   * Write methods may throw a BufferOverflowException if
   * the buffer is not large enough.  A large enough buffer
   * size must be chosen so that this does not happen or
   * the caller must be prepared to catch the exception and
   * try again once part of the buffer has been consumed.
   *
   *
   * @return the producer for this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  public Writer getWriter(){
    return writer;
  }
  /**
   * Retrieve a Reader that can be used to empty
   * this buffer.
   * <p>
   * This Reader supports marks at the expense
   * of the buffer size.
   *
   * @return the consumer for this buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  public Reader getReader(){
    return reader;
  }
  /**
   * Get number of characters that are available to be read.
   * <p>
   * Note that the number of characters available plus
   * the number of characters free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the size in characters of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getAvailable(){
    synchronized (this){
      return available();
    }
  }
  /**
   * Get the number of characters this buffer has free for
   * writing.
   * <p>
   * Note that the number of characters available plus
   * the number of characters free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the available space in characters of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getSpaceLeft(){
    synchronized (this){
      return spaceLeft();
    }
  }
  /**
   * Get the capacity of this buffer.
   * <p>
   * Note that the number of characters available plus
   * the number of characters free may not add up to the
   * capacity of this buffer, as the buffer may reserve some
   * space for other purposes.
   *
   * @return the size in characters of this buffer
   *
   * @since ostermillerutils 1.00.00
   */
  public int getSize(){
    synchronized (this){
      return buffer.length;
    }
  }
  /**
   * double the size of the buffer
   *
   * @since ostermillerutils 1.00.00
   */
  private void resize(){
    char[] newBuffer = new char[buffer.length * 2];
    int marked = marked();
    int available = available();
    if (markPosition <= writePosition){
      // any space between the mark and
      // the first write needs to be saved.
      // In this case it is all in one piece.
      int length = writePosition - markPosition;
      System.arraycopy(buffer, markPosition, newBuffer, 0, length);
    } else {
      int length1 = buffer.length - markPosition;
      System.arraycopy(buffer, markPosition, newBuffer, 0, length1);
      int length2 = writePosition;
      System.arraycopy(buffer, 0, newBuffer, length1, length2);
    }
    buffer = newBuffer;
    markPosition = 0;
    readPosition = marked;
    writePosition = marked + available;
  }
  /**
   * Space available in the buffer which can be written.
   *
   * @since ostermillerutils 1.00.00
   */
  private int spaceLeft(){
    if (writePosition < markPosition){
      // any space between the first write and
      // the mark except one character is available.
      // In this case it is all in one piece.
      return (markPosition - writePosition - 1);
    } else {
      // space at the beginning and end.
      return ((buffer.length - 1) - (writePosition - markPosition));
    }
  }
  /**
   * Characters available for reading.
   *
   * @since ostermillerutils 1.00.00
   */
  private int available(){
    if (readPosition <= writePosition){
      // any space between the first read and
      // the first write is available.  In this case i
      // is all in one piece.
      return (writePosition - readPosition);
    } else {
      // space at the beginning and end.
      return (buffer.length - (readPosition - writePosition));
    }
  }
  /**
   * Characters saved for supporting marks.
   *
   * @since ostermillerutils 1.00.00
   */
  private int marked(){
    if (markPosition <= readPosition){
      // any space between the markPosition and
      // the first write is marked.  In this case i
      // is all in one piece.
      return (readPosition - markPosition);
    } else {
      // space at the beginning and end.
      return (buffer.length - (markPosition - readPosition));
    }
  }
  /**
   * If we have passed the markSize reset the
   * mark so that the space can be used.
   *
   * @since ostermillerutils 1.00.00
   */
  private void ensureMark(){
    if (marked() >= markSize){
      markPosition = readPosition;
      markSize = 0;
    }
  }
  /**
   * Create a new buffer with a default capacity.
   * Writing to a full buffer will block until space
   * is available rather than throw an exception.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(){
    this (DEFAULT_SIZE, true);
  }
  /**
   * Create a new buffer with given capacity.
   * Writing to a full buffer will block until space
   * is available rather than throw an exception.
   * <p>
   * Note that the buffer may reserve some characters for
   * special purposes and capacity number of characters may
   * not be able to be written to the buffer.
   * <p>
   * Note that if the buffer is of INFINITE_SIZE it will
   * neither block or throw exceptions, but rather grow
   * without bound.
   *
   * @param size desired capacity of the buffer in characters or CircularCharBuffer.INFINITE_SIZE
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(int size){
    this (size, true);
  }
  /**
   * Create a new buffer with a default capacity and
   * given blocking behavior.
   *
   * @param blockingWrite true writing to a full buffer should block
   *        until space is available, false if an exception should
   *        be thrown instead.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(boolean blockingWrite){
    this (DEFAULT_SIZE, blockingWrite);
  }
  /**
   * Create a new buffer with the given capacity and
   * blocking behavior.
   * <p>
   * Note that the buffer may reserve some characters for
   * special purposes and capacity number of characters may
   * not be able to be written to the buffer.
   * <p>
   * Note that if the buffer is of CircularCharBuffer.INFINITE_SIZE it will
   * neither block or throw exceptions, but rather grow
   * without bound.
   *
   * @param size desired capacity of the buffer in characters or CircularCharBuffer.INFINITE_SIZE
   * @param blockingWrite true writing to a full buffer should block
   *        until space is available, false if an exception should
   *        be thrown instead.
   *
   * @since ostermillerutils 1.00.00
   */
  public CircularCharBuffer(int size, boolean blockingWrite){
    if (size == INFINITE_SIZE){
      buffer = new char[DEFAULT_SIZE];
      infinite = true;
    } else {
      buffer = new char[size];
      infinite = false;
    }
    this.blockingWrite = blockingWrite;
  }
  /**
   * Class for reading from a circular character buffer.
   *
   * @since ostermillerutils 1.00.00
   */
  protected class CircularCharBufferReader extends Reader {
    /**
     * Close the stream. Once a stream has been closed, further read(), ready(),
     * mark(), or reset() invocations will throw an IOException. Closing a
     * previously-closed stream, however, has no effect.
     *
     * @throws IOException never.
     *
     * @since ostermillerutils 1.00.00
     */
    public void close() throws IOException {
      synchronized (CircularCharBuffer.this){
        readerClosed = true;
      }
    }
    /**
     * Mark the present position in the stream. Subsequent calls to reset() will
     * attempt to reposition the stream to this point.
     * <p>
     * The readAheadLimit must be less than the size of circular buffer.
     *
     * @param readAheadLimit Limit on the number of characters that may be read while
     *    still preserving the mark. After reading this many characters, attempting to
     *    reset the stream will fail.
     * @throws IOException if the stream is closed, or the buffer size is greater
     * than or equal to the readAheadLimit.
     *
     * @since ostermillerutils 1.00.00
     */
    public void mark(int readAheadLimit) throws IOException {
      synchronized (CircularCharBuffer.this){
        if (readerClosed) throw new IOException("Reader has been closed; cannot mark a closed Reader.");
        if (buffer.length - 1 <= readAheadLimit) throw new IOException("Cannot mark stream, readAheadLimit bigger than buffer size.");
        markSize = readAheadLimit;
        markPosition = readPosition;
      }
    }
    /**
     * Tell whether this stream supports the mark() operation.
     *
     * @return true, mark is supported.
     *
     * @since ostermillerutils 1.00.00
     */
    public boolean markSupported() {
      return true;
    }
    /**
     * Read a single character.
     * This method will block until a character is available, an I/O error occurs,
     * or the end of the stream is reached.
     *
     * @return The character read, as an integer in the range 0 to 65535 (0x00-0xffff),
     *     or -1 if the end of the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    public int read() throws IOException {
      while (true){
        synchronized (CircularCharBuffer.this){
          if (readerClosed) throw new IOException("Reader has been closed; cannot read from a closed Reader.");
          int available = available();
          if (available > 0){
            int result = buffer[readPosition] & 0xffff;
            readPosition++;
            if (readPosition == buffer.length){
              readPosition = 0;
            }
            ensureMark();
            return result;
          } else if (writerClosed){
            return -1;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
    /**
     * Read characters into an array.
     * This method will block until some input is available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param cbuf Destination buffer.
     * @return The number of characters read, or -1 if the end of
     *   the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    public int read(char[] cbuf) throws IOException {
      return read(cbuf, 0, cbuf.length);
    }
    /**
     * Read characters into a portion of an array.
     * This method will block until some input is available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param cbuf Destination buffer.
     * @param off Offset at which to start storing characters.
     * @param len Maximum number of characters to read.
     * @return The number of characters read, or -1 if the end of
     *   the stream has been reached
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    public int read(char[] cbuf, int off, int len) throws IOException {
      while (true){
        synchronized (CircularCharBuffer.this){
          if (readerClosed) throw new IOException("Reader has been closed; cannot read from a closed Reader.");
          int available = available();
          if (available > 0){
            int length = Math.min(len, available);
            int firstLen = Math.min(length, buffer.length - readPosition);
            int secondLen = length - firstLen;
            System.arraycopy(buffer, readPosition, cbuf, off, firstLen);
            if (secondLen > 0){
              System.arraycopy(buffer, 0, cbuf, off+firstLen,  secondLen);
              readPosition = secondLen;
            } else {
              readPosition += length;
            }
            if (readPosition == buffer.length) {
              readPosition = 0;
            }
            ensureMark();
            return length;
          } else if (writerClosed){
            return -1;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
    /**
     * Tell whether this stream is ready to be read.
     *
     * @return True if the next read() is guaranteed not to block for input,
     *    false otherwise. Note that returning false does not guarantee that
     *    the next read will block.
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    public boolean ready() throws IOException {
      synchronized (CircularCharBuffer.this){
        if (readerClosed) throw new IOException("Reader has been closed, it is not ready.");
        return (available() > 0);
      }
    }
    /**
     * Reset the stream.
     * If the stream has been marked, then attempt to reposition i
     * at the mark. If the stream has not been marked, or more characters
     * than the readAheadLimit have been read, this method has no effect.
     *
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    public void reset() throws IOException {
      synchronized (CircularCharBuffer.this){
        if (readerClosed) throw new IOException("Reader has been closed; cannot reset a closed Reader.");
        readPosition = markPosition;
      }
    }
    /**
     * Skip characters.
     * This method will block until some characters are available,
     * an I/O error occurs, or the end of the stream is reached.
     *
     * @param n The number of characters to skip
     * @return The number of characters actually skipped
     * @throws IllegalArgumentException if n is negative.
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    public long skip(long n) throws IOException, IllegalArgumentException {
      while (true){
        synchronized (CircularCharBuffer.this){
          if (readerClosed) throw new IOException("Reader has been closed; cannot skip characters on a closed Reader.");
          int available = available();
          if (available > 0){
            int length = Math.min((int)n, available);
            int firstLen = Math.min(length, buffer.length - readPosition);
            int secondLen = length - firstLen;
            if (secondLen > 0){
              readPosition = secondLen;
            } else {
              readPosition += length;
            }
            if (readPosition == buffer.length) {
              readPosition = 0;
            }
            ensureMark();
            return length;
          } else if (writerClosed){
            return 0;
          }
        }
        try {
          Thread.sleep(100);
        } catch(Exception x){
          throw new IOException("Blocking read operation interrupted.");
        }
      }
    }
  }
  /**
   * Class for writing to a circular character buffer.
   * If the buffer is full, the writes will either block
   * until there is some space available or throw an IOException
   * based on the CircularCharBuffer"s preference.
   *
   * @since ostermillerutils 1.00.00
   */
  protected class CircularCharBufferWriter extends Writer {
    /**
     * Close the stream, flushing it first.
     * This will cause the reader associated with this circular buffer
     * to read its last characters once it empties the buffer.
     * Once a stream has been closed, further write() or flush() invocations
     * will cause an IOException to be thrown. Closing a previously-closed stream,
     * however, has no effect.
     *
     * @throws IOException never.
     *
     * @since ostermillerutils 1.00.00
     */
    public void close() throws IOException {
      synchronized (CircularCharBuffer.this){
        if (!writerClosed){
          flush();
        }
        writerClosed = true;
      }
    }
    /**
     * Flush the stream.
     *
     * @throws IOException if the stream is closed.
     *
     * @since ostermillerutils 1.00.00
     */
    public void flush() throws IOException {
      if (writerClosed) throw new IOException("Writer has been closed; cannot flush a closed Writer.");
      if (readerClosed) throw new IOException("Buffer closed by Reader; cannot flush.");
      // this method needs to do nothing
    }
    /**
     * Write an array of characters.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param cbuf Array of characters to be written
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    public void write(char[] cbuf) throws IOException {
      write(cbuf, 0, cbuf.length);
    }
    /**
     * Write a portion of an array of characters.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param cbuf Array of characters
     * @param off Offset from which to start writing characters
     * @param len - Number of characters to write
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    public void write(char[] cbuf, int off, int len) throws IOException {
      while (len > 0){
        synchronized (CircularCharBuffer.this){
          if (writerClosed) throw new IOException("Writer has been closed; cannot write to a closed Writer.");
          if (readerClosed) throw new IOException("Buffer closed by Reader; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < len){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < len) throw new BufferOverflowException("CircularCharBuffer is full; cannot write " + len + " characters");
          int realLen = Math.min(len, spaceLeft);
          int firstLen = Math.min(realLen, buffer.length - writePosition);
          int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1);
          int written = firstLen + secondLen;
          if (firstLen > 0){
            System.arraycopy(cbuf, off, buffer, writePosition, firstLen);
          }
          if (secondLen > 0){
            System.arraycopy(cbuf, off+firstLen, buffer, 0, secondLen);
            writePosition = secondLen;
          } else {
            writePosition += written;
          }
          if (writePosition == buffer.length) {
            writePosition = 0;
          }
          off += written;
          len -= written;
        }
        if (len > 0){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
    /**
     * Write a single character.
     * The character to be written is contained in the 16 low-order bits of the
     * given integer value; the 16 high-order bits are ignored.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param c int specifying a character to be written.
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    public void write(int c) throws IOException {
      boolean written = false;
      while (!written){
        synchronized (CircularCharBuffer.this){
          if (writerClosed) throw new IOException("Writer has been closed; cannot write to a closed Writer.");
          if (readerClosed) throw new IOException("Buffer closed by Reader; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < 1){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < 1) throw new BufferOverflowException("CircularCharBuffer is full; cannot write 1 character");
          if (spaceLeft > 0){
            buffer[writePosition] = (char)(c & 0xffff);
            writePosition++;
            if (writePosition == buffer.length) {
              writePosition = 0;
            }
            written = true;
          }
        }
        if (!written){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
    /**
     * Write a string.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param str String to be written
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    public void write(String str) throws IOException {
      write(str, 0, str.length());
    }
    /**
     * Write a portion of a string.
     * If the buffer allows blocking writes, this method will block until
     * all the data has been written rather than throw an IOException.
     *
     * @param str A String
     * @param off Offset from which to start writing characters
     * @param len Number of characters to write
     * @throws BufferOverflowException if buffer does not allow blocking writes
     *   and the buffer is full.  If the exception is thrown, no data
     *   will have been written since the buffer was set to be non-blocking.
     * @throws IOException if the stream is closed, or the write is interrupted.
     *
     * @since ostermillerutils 1.00.00
     */
    public void write(String str, int off, int len) throws IOException {
      while (len > 0){
        synchronized (CircularCharBuffer.this){
          if (writerClosed) throw new IOException("Writer has been closed; cannot write to a closed Writer.");
          if (readerClosed) throw new IOException("Buffer closed by Reader; cannot write to a closed buffer.");
          int spaceLeft = spaceLeft();
          while (infinite && spaceLeft < len){
            resize();
            spaceLeft = spaceLeft();
          }
          if (!blockingWrite && spaceLeft < len) throw new BufferOverflowException("CircularCharBuffer is full; cannot write " + len + " characters");
          int realLen = Math.min(len, spaceLeft);
          int firstLen = Math.min(realLen, buffer.length - writePosition);
          int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1);
          int written = firstLen + secondLen;
          for (int i=0; i<firstLen; i++){
            buffer[writePosition + i] = str.charAt(off+i);
          }
          if (secondLen > 0){
            for (int i=0; i<secondLen; i++){
              buffer[i] = str.charAt(off+firstLen+i);
            }
            writePosition = secondLen;
          } else {
            writePosition += written;
          }
          if (writePosition == buffer.length) {
            writePosition = 0;
          }
          off += written;
          len -= written;
        }
        if (len > 0){
          try {
            Thread.sleep(100);
          } catch(Exception x){
            throw new IOException("Waiting for available space in buffer interrupted.");
          }
        }
      }
    }
  }
}
/*
 * Buffer Overflow Exception
 * Copyright (C) 2002 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * See COPYING.TXT for details.
 */
/**
 * An indication that there was a buffer overflow.
 *
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.00.00
 */
 class BufferOverflowException extends IOException {
  /**
   * Create a new Exception
   *
   * @since ostermillerutils 1.00.00
   */
  public BufferOverflowException(){
    super();
  }
  /**
   * Create a new Exception with the given message.
   *
   * @param msg Error message.
   *
   * @since ostermillerutils 1.00.00
   */
  public BufferOverflowException(String msg){
    super(msg);
  }
}





Endian differences and data storage

   
// : c12:Endians.java
// Endian differences and data storage.
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Endians {
  public static void main(String[] args) {
    ByteBuffer bb = ByteBuffer.wrap(new byte[12]);
    bb.asCharBuffer().put("abcdef");
    System.out.println(toString(bb.array()));
    bb.rewind();
    bb.order(ByteOrder.BIG_ENDIAN);
    bb.asCharBuffer().put("abcdef");
    System.out.println(toString(bb.array()));
    bb.rewind();
    bb.order(ByteOrder.LITTLE_ENDIAN);
    bb.asCharBuffer().put("abcdef");
    System.out.println(toString(bb.array()));
  }
  static String toString(byte[] a) {
    StringBuffer result = new StringBuffer("[");
    for (int i = 0; i < a.length; i++) {
      result.append(a[i]);
      if (i < a.length - 1)
        result.append(", ");
    }
    result.append("]");
    return result.toString();
  }
} ///:~





Getting different representations from a ByteBuffer

   
// : c12:GetData.java
// Getting different representations from a ByteBuffer
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.
import java.nio.ByteBuffer;
public class GetData {
  private static final int BSIZE = 1024;
  public static void main(String[] args) {
    ByteBuffer bb = ByteBuffer.allocate(BSIZE);
    // Allocation automatically zeroes the ByteBuffer:
    int i = 0;
    while (i++ < bb.limit())
      if (bb.get() != 0)
        System.out.println("nonzero");
    System.out.println("i = " + i);
    bb.rewind();
    // Store and read a char array:
    bb.asCharBuffer().put("Howdy!");
    char c;
    while ((c = bb.getChar()) != 0)
      System.out.print(c + " ");
    System.out.println();
    bb.rewind();
    // Store and read a short:
    bb.asShortBuffer().put((short) 471142);
    System.out.println(bb.getShort());
    bb.rewind();
    // Store and read an int:
    bb.asIntBuffer().put(99471142);
    System.out.println(bb.getInt());
    bb.rewind();
    // Store and read a long:
    bb.asLongBuffer().put(99471142);
    System.out.println(bb.getLong());
    bb.rewind();
    // Store and read a float:
    bb.asFloatBuffer().put(99471142);
    System.out.println(bb.getFloat());
    bb.rewind();
    // Store and read a double:
    bb.asDoubleBuffer().put(99471142);
    System.out.println(bb.getDouble());
    bb.rewind();
  }
} ///:~





Manipulating ints in a ByteBuffer with an IntBuffer

   
// : c12:IntBufferDemo.java
// Manipulating ints in a ByteBuffer with an IntBuffer
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class IntBufferDemo {
  private static final int BSIZE = 1024;
  public static void main(String[] args) {
    ByteBuffer bb = ByteBuffer.allocate(BSIZE);
    IntBuffer ib = bb.asIntBuffer();
    // Store an array of int:
    ib.put(new int[] { 11, 42, 47, 99, 143, 811, 1016 });
    // Absolute location read and write:
    System.out.println(ib.get(3));
    ib.put(3, 1811);
    ib.rewind();
    while (ib.hasRemaining()) {
      int i = ib.get();
      if (i == 0)
        break; // Else we"ll get the entire buffer
      System.out.println(i);
    }
  }
} ///:~





Output Buffering

   
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;

// Buffering may speed up writes to a FileOutputStream, but not for 
// a FileWriter, because a FileWriter is
// an OutputStreamWriter, which already uses the buffering.
public class OutputStreamDemo {
  public static void main(String[] args) throws IOException {
    OutputStream os1 = new FileOutputStream("tmp1.dat");
    writeints("Unbuffered: ", 1000000, os1);
    OutputStream os2 = new BufferedOutputStream(new FileOutputStream(
        "tmp2.dat"));
    writeints("Buffered:   ", 1000000, os2);
    Writer wr1 = new FileWriter("tmp1.dat");
    writeints("Unbuffered: ", 1000000, wr1);
    Writer wr2 = new BufferedWriter(new FileWriter("tmp2.dat"));
    writeints("Buffered:   ", 1000000, wr2);
  }
  static void writeints(String msg, int count, OutputStream os)
      throws IOException {
    long currentTime = System.currentTimeMillis() ;
    for (int i = 0; i < count; i++)
      os.write(i & 255);
    os.close();
    System.out.println(msg + Long.toString(System.currentTimeMillis()-currentTime));
  }
  static void writeints(String msg, int count, Writer os) throws IOException {
    long currentTime = System.currentTimeMillis() ;
    for (int i = 0; i < count; i++)
      os.write(i & 255);
    os.close();
    System.out.println(msg + Long.toString(System.currentTimeMillis()-currentTime));
  }
}





Using Buffers

   
// : c12:UsingBuffers.java
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
public class UsingBuffers {
  private static void symmetricScramble(CharBuffer buffer) {
    while (buffer.hasRemaining()) {
      buffer.mark();
      char c1 = buffer.get();
      char c2 = buffer.get();
      buffer.reset();
      buffer.put(c2).put(c1);
    }
  }
  public static void main(String[] args) {
    char[] data = "UsingBuffers".toCharArray();
    ByteBuffer bb = ByteBuffer.allocate(data.length * 2);
    CharBuffer cb = bb.asCharBuffer();
    cb.put(data);
    System.out.println(cb.rewind());
    symmetricScramble(cb);
    System.out.println(cb.rewind());
    symmetricScramble(cb);
    System.out.println(cb.rewind());
  }
} ///:~