Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.cloudhopper.commons.util;
/*
* #%L
* ch-commons-util
* %%
* Copyright (C) 2012 Cloudhopper by Twitter
* %%
* Licensed 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.
* #L%
*/
import java.io.UnsupportedEncodingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class implements a capacity constrained ByteBuffer using a circular array
* of bytes. A circular array of bytes minimizes memory allocations when adding
* or removing bytes from the buffer.
*
* NOTE: This class is NOT thread-safe since its intended mainly for use inside
* a single thread -- such as reading from a socket or other connection.
*
* NOTE: This class does not auto grow the underlying buffer.
*
* All add() methods are the only way to copy information to this buffer. However,
* there are various ways to delete or remove data from this buffer. The delete()
* methods will erase data from the buffer without returning the removed bytes.
* This avoids allocating memory if you simply want to free up space in this buffer.
* The remove() methods will erase the data AND return the erased data as the
* return value. Use these methods when you need to get the data and delete it.
*
* The size of the underlying byte[] buffer is always capacity + 1. If you
* initialize this buffer requesting a capacity of 1024, please be aware that this
* class will actually allocate 1025 bytes. This is required to be able to internally
* track where the data ends inside the buffer. However, externally, it will
* look like only 1024 bytes are free, capacity, size, etc. At no time would
* 1025 be made available in a public method.
*
* This class is originally derived from a CircularByteBuffer obtained from
* http://awr.free.fr/ which was distributed with the Lesser GNU Public License.
* That class was written by Arnaud Witschger. This class has been heavily
* modified for a different usage.
*
* @author joelauer (twitter: @jjlauer or http://twitter.com/jjlauer)
*/
public class ByteBuffer {
private final static Logger logger = LoggerFactory.getLogger(ByteBuffer.class);
/** Default buffer capacity. */
private static final int DEFAULT_BUFFER_CAPACITY = 1023;
/** Maximum for integer numbers. */
private static final int MAXIMUM_INTEGER = 2147483647;
/** The circular buffer is a byte array. */
private byte[] buffer;
/** The current buffer reading position. */
private int currentReadPosition;
/** The current buffer writing position. */
private int currentWritePosition;
/** The current buffer real size. */
private int currentBufferSize;
/**
* Constructs a new instance of ByteBuffer with an
* initial default capacity of 1024 bytes (DEFAULT_BUFFER_CAPACITY)
*/
public ByteBuffer() {
try {
this.circularByteBufferInitializer(DEFAULT_BUFFER_CAPACITY+1, 0, 0, 0);
} catch (IllegalArgumentException e) {
logger.error("Impossible case reached constructing ByteBuffer");
}
}
/**
* Constructs a new instance of ByteBuffer with the specified
* capacity.
* @param capacity The buffer capacity. Must be >= 1.
*/
public ByteBuffer(int capacity) throws IllegalArgumentException {
// since we'll always allocate capacity+1, the user only needs to request
// a size of at least 1
if (capacity+1 < 2) {
throw new IllegalArgumentException("Buffer capacity must be >= 1");
}
this.circularByteBufferInitializer(capacity+1, 0, 0, 0);
}
/**
* Constructs a new instance of ByteBuffer with a capacity
* only large enough to hold they byte array. Buffer is initialized with
* the byte[] bytes starting from offset 0 and adding the entire byte aray.
* @param bytes The byte array to initialize buffer with
*/
public ByteBuffer(byte[] bytes) {
this((bytes.length==0 ? 1 : bytes.length));
try {
this.add(bytes);
} catch (BufferSizeException e) {
logger.error("Impossible case reached constructing ByteBuffer");
}
}
/**
* Constructs a new instance of ByteBuffer with the specified
* capacity. Buffer is initialized with the byte[] bytes starting from
* offset 0 and adding the entire byte aray. Capacity must be >= the length
* of the byte array.
* @param bytes The byte array to initialize buffer with
* @param capacity The buffer capacity. Must be >= 1.
* @throws IllegalArgumentException Thrown if the capacity is too small
*/
public ByteBuffer(byte[] bytes, int capacity)
throws IllegalArgumentException {
this(bytes, 0, bytes.length, capacity);
}
/**
* Constructs a new instance of ByteBuffer with the specified
* capacity. Buffer is initialized with the byte[] bytes starting from
* offset and added up to length bytes.
* @param bytes The byte array to initialize buffer with
* @param offset The offset in the byte array to start from
* @param length The length starting from offset within the byte array
* @throws IllegalArgumentException Thrown if offset or length is negative,
* capacity is too small, or if the offset+length would cause a read
* past the length of the byte array.
*/
public ByteBuffer(byte[] bytes, int offset, int length)
throws IllegalArgumentException {
this(bytes, offset, length, length);
}
/**
* Constructs a new instance of ByteBuffer with the specified
* capacity. Buffer is initialized with the byte[] bytes starting from
* offset and added up to length bytes. Capacity
* must be large enough to store the length.
* @param bytes The byte array to initialize buffer with
* @param offset The offset in the byte array to start from
* @param length The length starting from offset within the byte array
* @param capacity The buffer capacity. Must be >= 1.
* @throws IllegalArgumentException Thrown if offset or length is negative,
* capacity is too small, or if the offset+length would cause a read
* past the length of the byte array.
*/
public ByteBuffer(byte[] bytes, int offset, int length, int capacity)
throws IllegalArgumentException {
this(capacity); // will check for valid capacity
checkOffsetLength(bytes.length, offset, length);
// user should have requested >= the length of the bytes they passed in
if (capacity < length) {
throw new IllegalArgumentException("Buffer capacity (" + capacity + ") must be >= the byte[] length (" + bytes.length + ")");
}
// at this point, guaranteed to have enough capacity to add this
// entire byte array to this buffer -- we can at least prevent
// any BufferSizeException
try {
this.add(bytes, offset, length);
} catch (BufferSizeException e) {
logger.error("Impossible case of BufferSizeException in ByteBuffer constructor", e);
}
}
/**
* Constructs a new instance of ByteBuffer.
* Buffer is initialized with the String converted to bytes using the
* ISO-8859-1 character set.
* @param string0 The string to initialize our buffer with
* @throws java.lang.IllegalArgumentException If capacity is too small to store
* the bytes from the String
*/
public ByteBuffer(String string0) throws IllegalArgumentException {
this(string0, string0.length());
}
/**
* Constructs a new instance of ByteBuffer with the specified
* capacity. Buffer is initialized with the String converted to bytes using the
* ISO-8859-1 character set. Capacity must be large enough to store the
* bytes obtained after conversion. If only 8-bit data is stored in the String,
* the capacity required would be the length() of the String.
* @param string0 The string to initialize our buffer with
* @param capacity The capacity of the buffer. Must be >= byte length of string
* @throws java.lang.IllegalArgumentException If capacity is too small to store
* the bytes from the String
*/
public ByteBuffer(String string0, int capacity) throws IllegalArgumentException {
this(capacity);
byte[] bytes = null;
try {
// convert string to bytes via ISO-8859-1
bytes = string0.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
logger.error("Impossible case in BtyeBuffer(String) constructor since ISO-8859-1 should be supported encoding", e);
throw new IllegalArgumentException("Unsupported encoding exception thrown, should never happen", e);
}
// is the capacity large enough
if (capacity < bytes.length) {
throw new IllegalArgumentException("Buffer capacity (" + capacity + ") must be >= string0 length (" + string0.length() + "). ISO-8859-1 conversion to bytes > string length?");
}
try {
this.add(bytes);
} catch (BufferSizeException e) {
logger.error("Impossible case in BtyeBuffer(String) constructor since capacity should have been ensured", e);
throw new IllegalArgumentException("BufferSizeException exception thrown, should never happen", e);
}
}
/**
* Intializes the new CircularByteBuffer with all parameters that characterize a CircularByteBuffer.
* @param bufferCapacity the buffer capacity. Must be >= 2.
* @param bufferSize the buffer initial size. Must be in [0, bufferCapacity].
* @param readPosition the buffer initial read position. Must be in [0, bufferSize]
* @param writePosition the buffer initial write position. Must be in [0, bufferSize]
*/
private void circularByteBufferInitializer(int bufferCapacity, int bufferSize, int readPosition, int writePosition) {
if (bufferCapacity < 2) {
throw new IllegalArgumentException("Buffer capacity must be greater than 2 !");
}
if ((bufferSize < 0) || (bufferSize > bufferCapacity)) {
throw new IllegalArgumentException("Buffer size must be a value between 0 and "+bufferCapacity+" !");
}
if ((readPosition < 0) || (readPosition > bufferSize)) {
throw new IllegalArgumentException("Buffer read position must be a value between 0 and "+bufferSize+" !");
}
if ((writePosition < 0) || (writePosition > bufferSize)) {
throw new IllegalArgumentException("Buffer write position must be a value between 0 and "+bufferSize+" !");
}
this.buffer = new byte[bufferCapacity];
this.currentBufferSize = bufferSize;
this.currentReadPosition = readPosition;
this.currentWritePosition = writePosition;
}
/**
* Gets the modular exponentiation, i.e. result of a^b mod n. Use to calculate hashcode.
* @param a A number.
* @param b An exponent.
* @param n A modulus.
* @return Result of modular exponentiation, i.e. result of a^b mod n.
*/
private int modularExponentation(int a, int b, int n) throws IllegalArgumentException {
int result = 1;
int counter;
int maxBinarySize = 32;
boolean[] b2Binary = new boolean[maxBinarySize];
for (counter = 0; b > 0; counter++) {
if (counter >= maxBinarySize){
throw new IllegalArgumentException("Exponent "+b+" is too big !");
}
b2Binary[counter] = (b % 2 != 0);
b = b / 2;
}
for (int k = counter - 1; k >= 0; k--) {
result = (result * result) % n;
if (b2Binary[k]){
result = (result * a) % n;
}
}
return result;
}
/**
* Gets the current buffer size.
* @return The current buffer size.
*/
public int size() {
return this.currentBufferSize;
}
/**
* Gets the total buffer allocated capacity, not the remaining capacity.
* Using capacity()-size() would calculate how much room is remaining in
* this buffer.
* @return The buffer capacity.
* @see #free()
*/
public int capacity() {
// this is always 1 less than the actual byte[]
return this.buffer.length-1;
}
/**
* Gets the number of free bytes this buffer has remaining for writing.
* @return The number of bytes available for writing in this buffer.
*/
public int free() {
return capacity()-size();
}
/**
* Tests if the buffer has enough free space to store N bytes. Same
* as free() >= N.
* @return True if the buffer has enough free space to store N bytes,
* otherwise false.
*/
public boolean isFree(int count) {
if (count < 0) {
throw new IllegalArgumentException("Count cannot be negative");
}
return (free() >= count);
}
/**
* Tests if the buffer is empty.
* @return True if the buffer is empty, otherwise false.
*/
public boolean isEmpty() {
return (size() == 0);
}
/**
* Tests if the buffer is full.
* @return True if the buffer is full, otherwise false.
*/
public boolean isFull() {
return (size() == capacity());
}
/**
* Clears the buffer and resets it. Does not reallocate a new array.
*/
public void clear() {
this.currentReadPosition = 0;
this.currentWritePosition = 0;
this.currentBufferSize = 0;
}
/**
* Adds one byte to the buffer and throws an exception if the buffer is
* full.
* @param b Byte to add to the buffer.
* @throws BufferIsFullException If the buffer is full and the byte cannot
* be stored.
*/
public void add(byte b) throws BufferIsFullException {
if (isFull()) {
throw new BufferIsFullException("Buffer is full and has reached maximum capacity (" + capacity() + ")");
}
// buffer is not full
this.buffer[this.currentWritePosition] = b;
this.currentWritePosition = (this.currentWritePosition + 1) % this.buffer.length;
this.currentBufferSize += 1;
}
/**
* Adds a byte array to this buffer. If the free space remaining in the buffer
* is not large enough, this method will throw a BufferSizeException. A byte
* array consisting of a length of 0 will immediately return.
* @param bytes A byte array to add to this buffer. Its size must less than or equal to this buffer's free space
* @throws BufferSizeException If this buffer's free space is not large enough to store add the byte array
*/
public void add(byte[] bytes) throws BufferSizeException {
this.add(bytes, 0, bytes.length);
}
/**
* Helper method for validating if an offset and length are valid for a given
* byte array. Checks if the offset or length is negative or if the offset+length
* would cause a read past the end of the byte array.
* @param bytesLength The length of the byte array to validate against
* @param offset The offset within the byte array
* @param length The length to read starting from the offset
* @throws java.lang.IllegalArgumentException If any of the above conditions
* are violated.
*/
static protected void checkOffsetLength(int bytesLength, int offset, int length)
throws IllegalArgumentException {
// offset cannot be negative
if (offset < 0) {
throw new IllegalArgumentException("The byte[] offset parameter cannot be negative");
}
// length cannot be negative either
if (length < 0) {
throw new IllegalArgumentException("The byte[] length parameter cannot be negative");
}
// is it a valid offset? Must be < bytes.length if non-zero
// if its zero, then the check below will validate if it would cause
// a read past the length of the byte array
if (offset != 0 && offset >= bytesLength) {
throw new IllegalArgumentException("The byte[] offset (" + offset + ") must be < the length of the byte[] length (" + bytesLength + ")");
}
if (offset+length > bytesLength) {
throw new IllegalArgumentException("The offset+length (" + (offset+length) + ") would read past the end of the byte[] (length=" + bytesLength + ")");
}
}
/**
* Adds a byte array to this buffer starting from the offset up to the
* length requested. If the free space remaining in the buffer
* is not large enough, this method will throw a BufferSizeException.
* @param bytes A byte array to add to this buffer.
* @param offset The offset within the byte array to begin to add
* @param length The length starting from offset to begin to add
* @throws BufferSizeException If this buffer's free space is not large enough to store add the byte array
*/
public void add(byte[] bytes, int offset, int length)
throws IllegalArgumentException, BufferSizeException {
// validate the bytes, offset, length
checkOffsetLength(bytes.length, offset, length);
// is there enough free space in this buffer to add the entire array
if (length > free()) {
throw new BufferSizeException("Buffer does not have enough free space (" + free() + " bytes) to add " + length + " bytes of data");
}
// add each byte to this array
for (int i = 0; i < length; i++) {
try {
this.add(bytes[i+offset]);
} catch (BufferIsFullException e) {
// this should be an impossible case since we checked the size() above
logger.error("Buffer is full even though this method checked its size() ahead of time", e);
throw new BufferSizeException(e.getMessage());
}
}
}
/**
* Deletes the first N bytes of the buffer. Avoids allocating any memory
* since there is no return value for this method. If you want to delete
* and also see the deleted data as a return value, please use the remove()
* methods.
*/
public void delete(int count) throws BufferSizeException {
if ((count < 0) || (count > capacity())) {
throw new IllegalArgumentException("Can only delete between 0 and " + capacity() + " bytes from buffer, you passed in=" + count);
}
if (count > size()) {
throw new BufferSizeException("Buffer size (" + size() + ") not large enough to delete (" + count + ") bytes");
}
this.currentReadPosition = (this.currentReadPosition + count) % this.buffer.length;
this.currentBufferSize -= count;
}
/**
* Removes the first (oldest/head) byte in the buffer. If the buffer is empty,
* this method will throw a BufferIsEmptyException
* @return The first (oldest/head) byte in the buffer
* @throws BufferIsEmptyException If the buffer is empty and a byte cannot be
* removed.
*/
public byte remove() throws BufferIsEmptyException {
if (size() == 0) {
throw new BufferIsEmptyException("Buffer is empty and no bytes available to remove");
}
byte b = this.buffer[this.currentReadPosition];
this.currentReadPosition = (this.currentReadPosition + 1) % this.buffer.length;
this.currentBufferSize -= 1;
return b;
}
/**
* Removes a byte array of the first N bytes in the buffer. The parameter
* count must be a value between 1 and the capacity of the buffer, otherwise
* a runtime IllegalArgumentException will be thrown. If the N bytes
* requested is larger than the buffer size, then a BufferSizeException
* will be thrown.
* @param count The number of bytes to remove (a value between 1 and the buffer capacity).
* @return The first N bytes of the buffer.
* @throws BufferSizeException If the buffer is not large enough to fufill the request
* (if N is > size)
*/
public byte[] remove(int count) throws BufferSizeException {
if ((count < 0) || (count > capacity())) {
throw new IllegalArgumentException("Tried to remove " + count + " bytes from buffer. The count must be a value between 0 and " + capacity());
}
if (count > size()) {
throw new BufferSizeException("Buffer size (" + size() + ") not large enough to remove (" + count + ") bytes");
}
// allocate the new array we'll fill
byte[] removedBuffer = new byte[count];
int currentPos = 0;
// remove each byte :
for (int i = 0; i < count; i++, currentPos++) {
try {
removedBuffer[currentPos] = this.remove();
} catch (BufferIsEmptyException e) {
// this should be an impossible case since we checked the size() above
logger.error("Buffer is empty even though this method checked its size() ahead of time", e);
throw new BufferSizeException(e.getMessage());
}
}
return removedBuffer;
}
/**
* Counts the number of occurrences of the byte in this ByteBuffer.
* This method will not overlap any bytes during its search. For example,
* if you're search for bytes of "AA" in a buffer containing "AAA", this
* method will only return a value of 1.
* @param b The byte to search for
* @return -1 if byte is not found, otherwise the number of occurrences.
*/
public int occurrences(byte b) {
byte[] bytes = { b };
return occurrences(bytes);
}
/**
* Counts the number of occurrences of the byte array in this
* ByteBuffer. This method will not overlap any bytes during its
* search. For example, if you're search for bytes of "AA" in a buffer containing "AAA", this
* method will only return a value of 1.
* @param bytes The byte[] to search for
* @return -1 if bytes is a length of zero, otherwise the number of occurrences.
*/
public int occurrences(byte[] bytes) {
if (bytes.length == 0) {
return -1;
}
int occurrences = 0;
int currentPos = -1;
int offset = 0;
while (offset < size()) {
// search for the delimiter
currentPos = indexOf(bytes, offset);
if (currentPos < 0) {
break;
}
occurrences++;
offset = currentPos + bytes.length;
}
return occurrences;
}
/**
public ByteBuffer copy() {
return copy(0, size());
}
*/
/**
* Most efficient copy of this ByteBuffer. The internal buffer
* is copied to the new ByteBuffer using either 1 or 2 calls to System.arraycopy().
* @return The new ByteBuffer with copied data
*/
public ByteBuffer copy() {
return copy(0, size(), size());
}
/**
* Most efficient copy of this ByteBuffer. The internal buffer
* is copied to the new ByteBuffer using either 1 or 2 calls to System.arraycopy().
* @param offset The offset in the buffer to start from
* @param length The length from the offset
* @return The new ByteBuffer with copied data
*/
public ByteBuffer copy(int offset, int length) {
return copy(offset, length, length);
}
/**
* Most efficient copy of this ByteBuffer. The internal buffer
* is copied to the new ByteBuffer using either 1 or 2 calls to System.arraycopy().
* @param offset The offset in the buffer to start from
* @param length The length from the offset
* @param capacity The capacity of the new ByteBuffer. Must be >= this ByteBuffer's size()
* @return The new ByteBuffer with copied data
*/
public ByteBuffer copy(int offset, int length, int capacity) {
// if not copying any data, just return an empty buffer
if (length == 0) {
return new ByteBuffer(1);
}
// validate offset, length ok
ByteBuffer.checkOffsetLength(size(), offset, length);
// is the capacity large enough?
if (capacity < length) {
throw new IllegalArgumentException("Capacity must be large enough to hold copied data of size=" + length);
}
// create a new ByteBuffer with enough capacity
ByteBuffer copyBuffer = new ByteBuffer(capacity);
// initialize everything now
// copy this buffer's data into the target buffer -- already allocated memory
this.toArray(offset, length, copyBuffer.buffer, 0);
// setup other variables to be correct position now
copyBuffer.currentBufferSize = length;
copyBuffer.currentReadPosition = 0;
copyBuffer.currentWritePosition = length;
return copyBuffer;
}
/**
* Splits a ByteBuffer into a ByteBuffer array where each
* ByteBuffer in the array contains the sequence of bytes split along the
* byte delimiter. The underlying byte[] in this ByteBuffer is not affected.
* If the delimiter wasn't found, then the returned ByteBuffer will essentially
* be a copy of the current ByteBuffer.
* @param delimiter The byte[] that will be treated as the delimiter
* @return A new array of ByteBuffers or null if the delimiter was not found
*/
/**
public ByteBuffer[] split(byte[] delimiter) {
// is the delimiter ok?
if (delimiter.length == 0) {
throw new IllegalArgumentException("Delimiter byte[] must have a length > 0");
}
// find number of occurrences of this delimiter
int occurrences = occurrences(delimiter);
// safety check
if (occurrences < 0) {
throw new IllegalArgumentException("Impossible case of -1 occurrences of the delimiter");
}
// otherwise, we know how many ByteBuffer's we'll create - we'll always
// end up return occurrences+1 buffers
int tokens = occurrences+1;
ByteBuffer[] buffers = new ByteBuffer[tokens];
int i = 0;
int offset = 0;
int delimiterPos = -1;
// keep looping until the offset is larger than we want
while (offset < size()) {
logger.debug("in split loop for i=" + i + ",delimiterPos=" + delimiterPos + ",offset=" + offset + ",tokens=" + tokens + ",size=" + this.size());
// find the next delimiter occurrence
delimiterPos = indexOf(delimiter, offset);
logger.debug("found next delimiter at pos=" + delimiterPos);
// no occurrence found, copy rest of buffer
if (delimiterPos < 0) {
// last token, copy rest of this buffer
buffers[i] = this.copy(offset, size()-offset);
} else {
buffers[i] = this.copy(offset, delimiterPos-offset);
offset = delimiterPos + delimiter.length;
}
i++;
}
return buffers;
}
*/
/**
* Internal unchecked version of get(). Will only throw a Runtime exception
* which should happen as long as internally, you're careful of how you
* access the buffer.
*/
private byte getUnchecked(int index) {
return this.buffer[(this.currentReadPosition + index) % this.buffer.length];
}
/**
* Gets the byte at the given index relative to the beginning the circular
* buffer. The index must be a value between 0 and the buffer capacity.
* @param index The index of the byte relative to the beginning the buffer
* (a value between 0 and the the current size).
* @return The byte at the given position relative to the beginning the buffer.
* @throws BufferSizeException If the index is >= size()
*/
public byte get(int index) throws IllegalArgumentException, BufferSizeException {
if ((index < 0) || (index >= capacity())) {
throw new IllegalArgumentException("The buffer index must be a value between 0 and " + capacity() + "!");
}
if (index >= size()) {
throw new BufferSizeException("Index " + index + " is >= buffer size of " + size());
}
return this.buffer[(this.currentReadPosition + index) % this.buffer.length];
}
/**
* Replaces the byte at the given position relative to the beginning the circular buffer by the given value.
* The position must be a value between 0 and the current buffer size - 1.
* @param position a position relative to the beginning the circular buffer (a value between 0 and the
* the current buffer size - 1).
* @param value a byte value to put instead of the previous value.
*/
/** NOT NEEDED YET
public void replace(int position, byte value) throws IllegalArgumentException {
if ((position < 0) || (position >= this.buffer.length)) {
throw new IllegalArgumentException("The position must be a value between 0 and "+this.buffer.length+" - 1 = "+(this.buffer.length - 1)+" !");
}
this.buffer[(this.currentReadPosition + position) % this.buffer.length] = value;
}
*/
/**
* Gets a copy of the current buffer as byte array. Method will allocate
* only enough memory to hold a copy of the current buffer data.
* @return A byte array. Could be empty if this buffer's size() is zero.
*/
public byte[] toArray() {
return toArray(0, size());
}
/**
* Gets a copy of the current buffer as byte array, but only copies data
* starting from an offset and length. Method will allocate
* only enough memory to hold a copy of the slice of the current buffer data.
* @param offset The offset to start from
* @param length The length from the offset
* @return A byte array. Could be empty.
* @throws IllegalArgumentException If capacity isn't large enough enough
* to hold the new byte[]
*/
public byte[] toArray(int offset, int length) {
return toArray(offset, length, length);
}
/**
* Gets a copy of the current buffer as byte array, but the new byte[]
* has the specified capacity. Useful if you need to store additional bytes
* in the returned byte[] and dont' want to do an additional System.arraycopy()
* afterwards. Method will allocate memory to hold a copy of the current array
* and return it.
* @param offset The offset to start from
* @param length The length from the offset
* @param capacity The size of the new byte[]. Must be >= this buffer's size()
* @return A byte array. Could be empty.
* @throws IllegalArgumentException If capacity isn't large enough enough
* to hold the new byte[]
*/
public byte[] toArray(int offset, int length, int capacity) {
// validate the offset, length are ok
ByteBuffer.checkOffsetLength(size(), offset, length);
// will we have a large enough byte[] allocated?
if (capacity < length) {
throw new IllegalArgumentException("Capacity must be large enough to hold a byte[] of at least a size=" + length);
}
byte[] arrayCopy = new byte[capacity];
this.toArray(offset, length, arrayCopy, 0);
return arrayCopy;
}
/**
* Will copy data from this ByteBuffer's buffer into the targetBuffer. This
* method requires the targetBuffer to already be allocated with enough capacity
* to hold this ByteBuffer's data.
* @param offset The offset within the ByteBuffer to start copy from
* @param length The length from the offset to copy
* @param targetBuffer The target byte array we'll copy data into. Must already
* be allocated with enough capacity.
* @param targetOffset The offset within the target byte array to start from
* @throws IllegalArgumentException If the offset and length are invalid
* for this ByteBuffer, if the targetOffset and targetLength are invalid
* for the targetBuffer, or if if the targetBuffer's capacity is not
* large enough to hold the copied data.
*/
public void toArray(int offset, int length, byte[] targetBuffer, int targetOffset) {
// validate the offset, length are ok
ByteBuffer.checkOffsetLength(size(), offset, length);
// validate the offset, length are ok
ByteBuffer.checkOffsetLength(targetBuffer.length, targetOffset, length);
// will we have a large enough byte[] allocated?
if (targetBuffer.length < length) {
throw new IllegalArgumentException("TargetBuffer size must be large enough to hold a byte[] of at least a size=" + length);
}
// are we actually copying any data?
if (length > 0) {
// create adjusted versions of read and write positions based
// on the offset and length passed into this method
int offsetReadPosition = (this.currentReadPosition + offset) % this.buffer.length;
int offsetWritePosition = (this.currentReadPosition + offset + length) % this.buffer.length;
if (offsetReadPosition >= offsetWritePosition) {
System.arraycopy(
this.buffer,
offsetReadPosition,
targetBuffer,
targetOffset,
this.buffer.length - offsetReadPosition);
System.arraycopy(
this.buffer,
0,
targetBuffer,
targetOffset + this.buffer.length - offsetReadPosition,
offsetWritePosition);
} else {
System.arraycopy(
this.buffer,
offsetReadPosition,
targetBuffer,
targetOffset,
offsetWritePosition - offsetReadPosition);
}
}
}
/**
* Tests if the buffer starts with the byte array prefix. The byte array
* prefix must have a size >= this buffer's size.
* @return True if the buffer starts with the bytes array, otherwise false.
*/
public boolean startsWith(byte[] prefix) {
if ((prefix.length == 0) || (prefix.length > size())) {
// no match would be possible
return false;
}
boolean match = true;
int i = 0;
int j = this.currentReadPosition;
while (match && (i < prefix.length)) {
if (this.buffer[j] != prefix[i]) {
match = false;
}
i++;
j = (j + 1) % this.buffer.length;
}
return match;
}
/**
* Tests if the buffer ends with the bytes array prefix. The byte array
* prefix must have a size >= this buffer's size.
* @return True if the buffer ends with the bytes array, otherwise false.
*/
public boolean endsWith(byte[] prefix) {
//logger.debug("Inside endsWith()");
if ((prefix.length == 0) || (prefix.length > size())) {
//It could not match :
return false;
}
boolean match = true;
int i = prefix.length - 1;
int j = (this.currentWritePosition - 1 + this.buffer.length) % this.buffer.length;
while (match && (i >= 0)) {
//logger.debug("endsWith() this.buffer["+j+"]=" + this.buffer[j] + "='" + (char)this.buffer[j] + "', prefix["+i+"]=" + prefix[i] + "='" + (char)prefix[i] + "'");
if (this.buffer[j] != prefix[i]) {
match = false;
}
i--;
j = (j - 1 + this.buffer.length) % this.buffer.length;
}
return match;
}
/**
* Returns the index within this buffer of the first occurrence of the
* specified bytes. The bytes to search for must have a size lower than
* or equal to the current buffer size. This method will return -1 if the
* bytes are not contained within this byte buffer.
* @param bytes The byte array to search for
* @return The index where the bytes start to match within the buffer.
*/
public int indexOf(byte[] bytes) {
return indexOf(bytes, 0);
}
/**
* Returns the index within this buffer of the first occurrence of the
* specified bytes after the offset. The bytes to search for must have a
* size lower than or equal to the current buffer size. This method will
* return -1 if the bytes are not contained within this byte buffer.
* @param bytes The byte array to search for
* @param offset The offset within the buffer to search from
* @return The index where the bytes start to match within the buffer.
*/
public int indexOf(byte[] bytes, int offset) {
// make sure we're not checking against any byte arrays of length 0
if (bytes.length == 0 || size() == 0) {
// a match is not possible
return -1;
}
// make sure the offset won't cause us to read past the end of the byte[]
if (offset < 0 || offset >= size()) {
throw new IllegalArgumentException("Offset must be a value between 0 and " + size() + " (current buffer size)");
}
int length = size()-bytes.length;
for (int i = offset; i <= length; i++) {
int j = 0;
// continue search loop while the next bytes match
while (j < bytes.length && getUnchecked(i+j) == bytes[j]) {
j++;
}
// if we found it, then j will equal the length of the search bytes
if (j == bytes.length) {
return i;
}
}
// if we get here then we didn't find it
return -1;
}
/**
* Gets a hash code for this ByteBuffer object based on actual sequence
* of bytes stored in this buffer. That means this object is safe to
* store in Hashtables or Hashmaps since the same sequence of bytes in
* different instances will generate the same hashCode, regardless of
* capacity, internal read position, write position, etc.
*
* This method is implemented using logic from Jakarta Commons Lang
* library in the HashCodeBuilder class.
* @return A hash code for this ByteBuffer object for the sequence of bytes
* stored in this buffer.
*/
@Override
public int hashCode() {
// use two hard-coded constant -- any value would do
final int hashingConstant = 37;
int hashCode = 17 * hashingConstant;
int size = size();
// adding entire array of bytes in sequence
for (int i = 0; i < size; i++) {
hashCode = hashCode * hashingConstant + getUnchecked(i);
}
return hashCode;
}
/**
* Tests if the current ByteBuffer and the parameter are both ByteBuffers
* and whether it contains the same sequence of bytes.
* @return True if the two objects are equal and contain the same sequence
* of bytes, otherwise false.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || !(obj instanceof ByteBuffer)) {
return false;
}
ByteBuffer toTest = (ByteBuffer)obj;
// can't be equal if size()'s don't match
if (toTest.size() != this.size())
return false;
// start comparison
int length = this.size();
for (int i = 0; i < length; i++) {
try {
if (this.get(i) != toTest.get(i)) {
return false;
}
} catch (Exception e) {
logger.error("Impossible case should never happen", e);
return false;
}
}
return true;
}
/**
* Tests if the current ByteBuffer and the byte array have the same
* sequence of bytes. Nearly the same as calling startsWith(), but if
* the two byte[]'s are a length of zero, this method will return true.
* @return True if the two byte arrays are equal and contain the same sequence
* of bytes, otherwise false.
*/
public boolean equals(byte[] bytes) {
// cannot be equal if the bytes is null, since this ByteBuffer is
// not definitely not null
if (bytes == null) {
return false;
}
// are both buffer's zero length, this is the case that startsWith misses
if (bytes.length == 0 && size() == 0) {
return true;
}
// otherwise, the impl is the same as startsWith
return this.startsWith(bytes);
}
/**
* Clones this ByteBuffer. Copies the buffer, data in the buffer, and keeps
* the original capacity value intact.
* @return A clone of this CircularByteBuffer object.
*/
/**
public Object clone() throws CloneNotSupportedException {
ByteBuffer clonedBuffer = null;
try {
clonedBuffer = new CircularByteBuffer(
this.buffer,
this.currentBufferSize,
this.currentReadPosition,
this.currentWritePosition);
} catch (IllegalArgumentException ue) {
//Nothing to do : impossible case !
}
return cbb;
}
*/
/**
* Returns a string representation of the current buffer. This converts
* any internal stored bytes directly to chars and appends them to a String.
* @return A byte-to-char string representation of the current buffer.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size(); i++) {
sb.append((char)getUnchecked(i));
}
return sb.toString();
}
/**
* Return a hexidecimal String representation of the current buffer with each byte
* displayed in a 2 character hexidecimal format. Useful for debugging.
* Convert a ByteBuffer to a String with a hexidecimal format.
* @return The string in hex representation
*/
public String toHexString() {
return toHexString(0, size());
}
/**
* Return a hexidecimal String representation of the current buffer with each byte
* displayed in a 2 character hexidecimal format. Useful for debugging.
* Convert a ByteBuffer to a String with a hexidecimal format.
* @param offset
* @param length
* @return The string in hex representation
*/
public String toHexString(int offset, int length) {
// is offset, length valid for the current size() of our internal byte[]
checkOffsetLength(size(), offset, length);
// if length is 0, return an empty string
if (length == 0 || size() == 0) {
return "";
}
StringBuilder s = new StringBuilder(length * 2);
int end = offset + length;
for (int i = offset; i < end; i++) {
HexUtil.appendHexString(s, getUnchecked(i));
}
return s.toString();
}
}