package com.pugh.sockso.web;
import java.io.IOException;
import java.io.InputStream;
import java.io.DataInputStream;
/**
* a buffer over the input that provides some convenience methods
* for accessing it's data
*
*/
public class InputBuffer {
private static final int BUFFER_MAX_SIZE = 1024;
private static final int DEFAULT_END_OF_STREAM_WAIT = 100;
private final DataInputStream in;
private final int endOfStreamWait;
private int[] queue;
private int head, tail, read;
/**
* Creates a new InputBuffer
*
* @param in
*
*/
public InputBuffer( final InputStream in ) {
this( in, DEFAULT_END_OF_STREAM_WAIT );
}
/**
* creates a new InputBuffer that pauses at the end of the stream for the
* specified number of millis before giving up.
*
* @param in
*
*/
public InputBuffer( final InputStream in, int endOfStreamWait ) {
this.in = new DataInputStream( in );
this.endOfStreamWait = endOfStreamWait;
queue = new int[ BUFFER_MAX_SIZE ];
head = 0;
tail = 0;
read = 0;
}
/**
* reads an int from the input stream, returns -1 if no more data
*
* @return
*
* @throws java.io.IOException
*
*/
public int read() throws IOException {
return head != tail
? queue[ head++ ]
: readDirectly();
}
/**
* reads from the buffer directly bypassing the internal stack
*
* @return
*
* @throws java.io.IOException
*
*/
public int readDirectly() throws IOException {
// if we've read some data, but it looks like we may
// be at the end of the stream, wait a little to check...
if ( read > 0 && in.available() == 0 ) {
try { Thread.sleep( endOfStreamWait ); }
catch ( final InterruptedException e ) {}
}
return ( read++ > 0 && in.available() == 0 )
? -1
: in.read();
}
/**
* puts an int back onto the input buffer, this will then be re-read by
* the read() method.
*
* @param i
*
*/
public void putBack( final int i ) {
if ( tail >= queue.length ) {
int[] newQueue = new int[ queue.length + BUFFER_MAX_SIZE ];
System.arraycopy( queue, head, newQueue, 0, tail - head );
queue = newQueue;
tail -= head;
head = 0;
}
queue[ tail++ ] = i;
}
/**
* tries to fetch a "line" from the buffer, where a line is a sequence of
* characters terminated by either "\n", "\r\n", or the end of the buffer
*
* @param stream
*
* @return
*
*/
public String readLine() throws IOException {
final StringBuffer sb = new StringBuffer();
char l = '\0';
while ( true ) {
final int i = read();
// end of stream?
if ( i == -1 ) {
return sb.toString();
}
final char c = (char) i;
// reached end of line?
if ( c == '\n' ) {
final String s = sb.toString();
// if the last but one character is a \r then we've got
// a proper HTTP EOL, goodness.
return s.substring( 0, s.length() - (l=='\r'?1:0) );
}
sb.append( c );
l = c;
}
}
/**
* returns whatever is left to be read from the buffer as a String
*
* @return
*
*/
public String readString() throws IOException {
return readString( -1 );
}
/**
* tries to return the specified amount of data from the buffer, but will
* return less if it hits the end. -1 will mean all rest of the buffer
* is returned.
*
* @param length
*
* @return
*
*/
public String readString( final int length ) throws IOException {
final StringBuffer sb = new StringBuffer();
int lengthRead = 0;
int i = read();
while ( i != -1 && (length == -1 || lengthRead++ < length) ) {
sb.append( (char) i );
i = read();
}
return sb.toString();
}
/**
* can be used to skip come int in the buffer
*
* @param charCount
*
*/
public void skip( final int amount ) throws IOException {
int removeAmount = amount;
if ( head != tail ) {
while ( true ) {
if ( removeAmount > 0 ) {
removeAmount--;
if ( head != tail ) {
head++;
}
else break;
}
else break;
}
}
in.skip( removeAmount );
}
}