Java/File Input Output/Buffering
Содержание
- 1 Buffered copying
- 2 Char Buffer
- 3 Circular Byte Buffer
- 4 Circular Char Buffer
- 5 Circular Char Buffer from http://ostermiller.org
- 6 Endian differences and data storage
- 7 Getting different representations from a ByteBuffer
- 8 Manipulating ints in a ByteBuffer with an IntBuffer
- 9 Output Buffering
- 10 Using Buffers
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());
}
} ///:~