io.pkts.buffer.AbstractBuffer Maven / Gradle / Ivy
/**
*
*/
package io.pkts.buffer;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* @author [email protected]
*/
public abstract class AbstractBuffer implements Buffer {
private static final String FOR_INPUT_STRING = "For input string: \"";
private static final byte LF = '\n';
private static final byte CR = '\r';
private static final String EMPTY_BUFFER_CANT_WRITE = "This is an empty buffer. Cant write to it";
/**
* From where we will continue reading
*/
protected int readerIndex;
/**
* This is where we will write the next byte.
*/
protected int writerIndex;
/**
* The position of the reader index that has been marked. I.e., this is the
* position we will move the reader index back to if someone is asking us to
* {@link #resetReaderIndex()}
*/
protected int markedReaderIndex;
/**
* We will pretend that any bytes below this boundary doesn't exist.
*/
protected int lowerBoundary;
/**
* Any bytes above this boundary is not accessible to us
*/
protected int upperBoundary;
// protected AbstractBuffer(final int readerIndex, final int lowerBoundary,
// final int upperBoundary,
// final byte[] buffer) {
protected AbstractBuffer(final int readerIndex, final int lowerBoundary, final int upperBoundary,
final int writerIndex) {
assert lowerBoundary <= upperBoundary;
this.readerIndex = readerIndex;
this.markedReaderIndex = readerIndex;
this.lowerBoundary = lowerBoundary;
this.upperBoundary = upperBoundary;
this.writerIndex = writerIndex;
}
@Override
public abstract Buffer clone();
/**
* {@inheritDoc}
*/
@Override
public int getReaderIndex() {
return this.readerIndex;
}
@Override
public int getWriterIndex() {
return this.writerIndex;
}
@Override
public void setReaderIndex(final int index) {
this.readerIndex = index;
}
/**
* {@inheritDoc}
*/
@Override
public int capacity() {
return this.upperBoundary - this.lowerBoundary;
}
@Override
public int getWritableBytes() {
return this.upperBoundary - this.writerIndex;
}
@Override
public boolean hasWritableBytes() {
return getWritableBytes() > 0;
}
/**
* {@inheritDoc}
*/
@Override
public void markReaderIndex() {
this.markedReaderIndex = this.readerIndex;
}
/**
* {@inheritDoc}
*/
@Override
public Buffer slice(final int stop) {
return this.slice(getReaderIndex(), stop);
}
@Override
public Buffer slice() {
if (!hasReadableBytes()) {
return Buffers.EMPTY_BUFFER;
}
return this.slice(getReaderIndex(), getWriterIndex() - this.lowerBoundary);
}
/**
*
* {@inheritDoc}
*/
@Override
public int getReadableBytes() {
return this.writerIndex - this.readerIndex - this.lowerBoundary;
}
/**
* {@inheritDoc}
*/
@Override
public void resetReaderIndex() {
this.readerIndex = this.markedReaderIndex;
}
/**
* {@inheritDoc}
*/
@Override
public Buffer readUntil(final byte b) throws IOException, ByteNotFoundException {
return this.readUntil(4096, b);
}
/**
* {@inheritDoc}
*/
@Override
public Buffer readUntil(final int maxBytes, final byte... bytes) throws IOException, ByteNotFoundException,
IllegalArgumentException {
final int index = indexOf(maxBytes, bytes);
if (index == -1) {
throw new ByteNotFoundException(bytes);
}
final int size = index - getReaderIndex();
Buffer result = null;
if (size == 0) {
result = Buffers.EMPTY_BUFFER;
} else {
result = readBytes(size);
}
readByte(); // consume the one at the index as well
return result;
}
/**
* {@inheritDoc}
*/
@Override
public int indexOf(final byte b) throws IOException, ByteNotFoundException, IllegalArgumentException {
return this.indexOf(4096, b);
}
/**
* {@inheritDoc}
*/
@Override
public int indexOf(final int maxBytes, final byte... bytes) throws IOException, ByteNotFoundException,
IllegalArgumentException {
if (bytes.length == 0) {
throw new IllegalArgumentException("No bytes specified. Not sure what you want me to look for");
}
final int start = getReaderIndex();
int index = -1;
while (hasReadableBytes() && getReaderIndex() - start < maxBytes && index == -1) {
if (isByteInArray(readByte(), bytes)) {
index = this.readerIndex - 1;
}
}
this.readerIndex = start;
if (getReaderIndex() - start >= maxBytes) {
throw new ByteNotFoundException(maxBytes, bytes);
}
return index;
}
private boolean isByteInArray(final byte b, final byte[] bytes) {
for (final byte x : bytes) {
if (x == b) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public Buffer readLine() throws IOException {
final int start = this.readerIndex;
boolean foundCR = false;
while (hasReadableBytes()) {
final byte b = readByte();
switch (b) {
case LF:
return slice(start, this.readerIndex - (foundCR ? 2 : 1));
case CR:
foundCR = true;
break;
default:
if (foundCR) {
--this.readerIndex;
return slice(start, this.lowerBoundary + this.readerIndex - 1);
}
}
}
// i guess there were nothing for us to read
if (start >= this.readerIndex) {
return null;
}
return slice(start, this.readerIndex);
}
@Override
public Buffer readUntilDoubleCRLF() throws IOException {
final int start = this.readerIndex;
int found = 0;
while (found < 4 && hasReadableBytes()) {
final byte b = readByte();
if ((found == 0 || found == 2) && b == CR) {
++found;
} else if ((found == 1 || found == 3) && b == LF) {
++found;
} else {
found = 0;
}
}
if (found == 4) {
return slice(start, this.readerIndex - 4);
} else {
this.readerIndex = start;
return null;
}
}
/**
* Convenience method for checking if we have enough readable bytes
*
* @param length
* the length the user wishes to read
* @throws IndexOutOfBoundsException
* in case we don't have the bytes available
*/
protected void checkReadableBytes(final int length) throws IndexOutOfBoundsException {
if (!checkReadableBytesSafe(length)) {
throw new IndexOutOfBoundsException("Not enough readable bytes");
}
}
/**
* Convenience method for checking if we have enough readable bytes
*
* @param length
* the length the user wishes to read
* @return true if we have enough bytes available for read
*/
protected boolean checkReadableBytesSafe(final int length) {
return getReadableBytes() >= length;
}
/**
* Convenience method for checking if we can read at the index
*
* @param index
* @throws IndexOutOfBoundsException
*/
protected void checkIndex(final int index) throws IndexOutOfBoundsException {
if (index >= this.lowerBoundary + capacity()) {
//if (index >= this.lowerBoundary + this.writerIndex) {
throw new IndexOutOfBoundsException();
}
}
/**
* Check whether we have enough space for writing the desired length.
*
* @param length
* @return
*/
protected boolean checkWritableBytesSafe(final int length) {
return getWritableBytes() >= length;
}
/**
* Convenience method for checking whether we can write at the specified
* index.
*
* @param index
* @throws IndexOutOfBoundsException
*/
protected void checkWriterIndex(final int index) throws IndexOutOfBoundsException {
if (index < this.writerIndex || index >= this.upperBoundary) {
throw new IndexOutOfBoundsException();
}
}
/**
* {@inheritDoc}
*/
@Override
public final short readUnsignedByte() throws IndexOutOfBoundsException, IOException {
return (short) (readByte() & 0xFF);
}
/**
* The underlying subclass should override this if it has write support.
* {@inheritDoc}
*/
@Override
public boolean hasWriteSupport() {
return false;
}
@Override
public void write(final byte b) throws IndexOutOfBoundsException {
throw new WriteNotSupportedException(EMPTY_BUFFER_CANT_WRITE);
}
@Override
public void write(final String s) throws IndexOutOfBoundsException, WriteNotSupportedException,
UnsupportedEncodingException {
throw new WriteNotSupportedException(EMPTY_BUFFER_CANT_WRITE);
}
@Override
public void write(final String s, final String charset) throws IndexOutOfBoundsException,
WriteNotSupportedException, UnsupportedEncodingException {
throw new WriteNotSupportedException(EMPTY_BUFFER_CANT_WRITE);
}
@Override
public abstract boolean equals(Object other);
@Override
public abstract int hashCode();
@Override
public int parseToInt() throws NumberFormatException, IOException {
return parseToInt(10);
}
/**
* (Copied from the Integer class and slightly altered to read from this
* buffer instead of a String)
*
* Parses the string argument as a signed integer in the radix specified by
* the second argument. The characters in the string must all be digits of
* the specified radix (as determined by whether
* {@link java.lang.Character#digit(char, int)} returns a nonnegative
* value), except that the first character may be an ASCII minus sign
* '-'
('\u002D'
) to indicate a negative
* value. The resulting integer value is returned.
*
* An exception of type NumberFormatException
is thrown if any
* of the following situations occurs:
*
* - The first argument is
null
or is a string of length
* zero.
* - The radix is either smaller than
* {@link java.lang.Character#MIN_RADIX} or larger than
* {@link java.lang.Character#MAX_RADIX}.
*
- Any character of the string is not a digit of the specified radix,
* except that the first character may be a minus sign
'-'
(
* '\u002D'
) provided that the string is longer than length
* 1.
* - The value represented by the string is not a value of type
*
int
.
*
*
* Examples:
*
*
* parseInt("0", 10) returns 0
* parseInt("473", 10) returns 473
* parseInt("-0", 10) returns 0
* parseInt("-FF", 16) returns -255
* parseInt("1100110", 2) returns 102
* parseInt("2147483647", 10) returns 2147483647
* parseInt("-2147483648", 10) returns -2147483648
* parseInt("2147483648", 10) throws a NumberFormatException
* parseInt("99", 8) throws a NumberFormatException
* parseInt("Kona", 10) throws a NumberFormatException
* parseInt("Kona", 27) returns 411787
*
*
*
*
* @param s
* the String
containing the integer representation
* to be parsed
* @param radix
* the radix to be used while parsing s
.
* @return the integer represented by the string argument in the specified
* radix.
* @exception NumberFormatException
* if the String
does not contain a parsable
* int
.
*/
@Override
public int parseToInt(final int radix) throws NumberFormatException, IOException {
if (getReadableBytes() == 0) {
throw new NumberFormatException("Buffer is empty, cannot convert it to an integer");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix + " less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX");
}
int result = 0;
boolean negative = false;
int i = this.readerIndex;
final int max = getReadableBytes() + this.readerIndex;
int limit;
int multmin;
int digit;
if (max > 0) {
if (getByte(0) == (byte) '-') {
negative = true;
limit = Integer.MIN_VALUE;
i++;
} else {
limit = -Integer.MAX_VALUE;
}
multmin = limit / radix;
if (i < max) {
digit = Character.digit((char) getByte(i++), radix);
if (digit < 0) {
throw new NumberFormatException(FOR_INPUT_STRING + this + "\"");
} else {
result = -digit;
}
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit((char) getByte(i++), radix);
if (digit < 0) {
throw new NumberFormatException(FOR_INPUT_STRING + this + "\"");
}
if (result < multmin) {
throw new NumberFormatException(FOR_INPUT_STRING + this + "\"");
}
result *= radix;
if (result < limit + digit) {
throw new NumberFormatException(FOR_INPUT_STRING + this + "\"");
}
result -= digit;
}
} else {
throw new NumberFormatException(FOR_INPUT_STRING + this + "\"");
}
if (negative) {
if (i > 1) {
return result;
} else { /* Only got "-" */
throw new NumberFormatException(FOR_INPUT_STRING + this + "\"");
}
} else {
return -result;
}
}
}