* The removal order of an UnboundedFifoByteBuffer
is based on the insertion
* order; elements are removed in the same order in which they were added.
* The iteration order is the same as the removal order.
*
* The {@link #remove()} and {@link #get()} operations perform in constant time. * The {@link #add(Object)} operation performs in amortized constant time. All * other operations perform in linear time or worse. *
* Note that this implementation is not synchronized. The following can be
* used to provide synchronized access to your UnboundedFifoByteBuffer
:
*
* Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer()); **
* This buffer prevents null objects from being added. * * @since Commons Collections 3.0 (previously in main package v2.1) */ class UnboundedFifoByteBuffer { protected byte[] buffer; protected int head; protected int tail; /** * Constructs an UnboundedFifoByteBuffer with the default number of elements. * It is exactly the same as performing the following: * *
* new UnboundedFifoByteBuffer(32); **/ public UnboundedFifoByteBuffer() { this(32); } /** * Constructs an UnboundedFifoByteBuffer with the specified number of elements. * The integer must be a positive integer. * * @param initialSize the initial size of the buffer * @throws IllegalArgumentException if the size is less than 1 */ public UnboundedFifoByteBuffer(int initialSize) { if (initialSize <= 0) { throw new IllegalArgumentException("The size must be greater than 0"); } buffer = new byte[initialSize + 1]; head = 0; tail = 0; } /** * Returns the number of elements stored in the buffer. * * @return this buffer's size */ public int size() { int size = 0; if (tail < head) { size = buffer.length - head + tail; } else { size = tail - head; } return size; } /** * Returns true if this buffer is empty; false otherwise. * * @return true if this buffer is empty */ public boolean isEmpty() { return (size() == 0); } /** * Adds the given element to this buffer. * * @param b the byte to add * @return true, always */ public boolean add(final byte b) { if (size() + 1 >= buffer.length) { byte[] tmp = new byte[((buffer.length - 1) * 2) + 1]; int j = 0; for (int i = head; i != tail;) { tmp[j] = buffer[i]; buffer[i] = 0; j++; i++; if (i == buffer.length) { i = 0; } } buffer = tmp; head = 0; tail = j; } buffer[tail] = b; tail++; if (tail >= buffer.length) { tail = 0; } return true; } /** * Returns the next object in the buffer. * * @return the next object in the buffer * @throws BufferUnderflowException if this buffer is empty */ public byte get() { if (isEmpty()) { throw new IllegalStateException("The buffer is already empty"); } return buffer[head]; } /** * Removes the next object from the buffer * * @return the removed object * @throws BufferUnderflowException if this buffer is empty */ public byte remove() { if (isEmpty()) { throw new IllegalStateException("The buffer is already empty"); } byte element = buffer[head]; head++; if (head >= buffer.length) { head = 0; } return element; } /** * Increments the internal index. * * @param index the index to increment * @return the updated index */ private int increment(int index) { index++; if (index >= buffer.length) { index = 0; } return index; } /** * Decrements the internal index. * * @param index the index to decrement * @return the updated index */ private int decrement(int index) { index--; if (index < 0) { index = buffer.length - 1; } return index; } /** * Returns an iterator over this buffer's elements. * * @return an iterator over this buffer's elements */ public Iterator
Base64OutputStream
that writes the encoded data
* to the given output stream using the default line length (76) and line
* separator (CRLF).
*
* @param out
* underlying output stream.
*/
public Base64OutputStream(OutputStream out) {
this(out, DEFAULT_LINE_LENGTH, CRLF_SEPARATOR);
}
/**
* Creates a Base64OutputStream
that writes the encoded data
* to the given output stream using the given line length and the default
* line separator (CRLF).
*
* The given line length will be rounded up to the nearest multiple of 4. If
* the line length is zero then the output will not be split into lines.
*
* @param out
* underlying output stream.
* @param lineLength
* desired line length.
*/
public Base64OutputStream(OutputStream out, int lineLength) {
this(out, lineLength, CRLF_SEPARATOR);
}
/**
* Creates a Base64OutputStream
that writes the encoded data
* to the given output stream using the given line length and line
* separator.
*
* The given line length will be rounded up to the nearest multiple of 4. If * the line length is zero then the output will not be split into lines and * the line separator is ignored. *
* The line separator must not include characters from the BASE64 alphabet
* (including the padding character =
).
*
* @param out
* underlying output stream.
* @param lineLength
* desired line length.
* @param lineSeparator
* line separator to use.
*/
public Base64OutputStream(OutputStream out, int lineLength,
byte[] lineSeparator) {
super(out);
if (out == null)
throw new IllegalArgumentException();
if (lineLength < 0)
throw new IllegalArgumentException();
checkLineSeparator(lineSeparator);
this.lineLength = lineLength;
this.lineSeparator = new byte[lineSeparator.length];
System.arraycopy(lineSeparator, 0, this.lineSeparator, 0,
lineSeparator.length);
this.encoded = new byte[ENCODED_BUFFER_SIZE];
}
@Override
public final void write(final int b) throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
singleByte[0] = (byte) b;
write0(singleByte, 0, 1);
}
@Override
public final void write(final byte[] buffer) throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
if (buffer == null)
throw new NullPointerException();
if (buffer.length == 0)
return;
write0(buffer, 0, buffer.length);
}
@Override
public final void write(final byte[] buffer, final int offset,
final int length) throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
if (buffer == null)
throw new NullPointerException();
if (offset < 0 || length < 0 || offset + length > buffer.length)
throw new IndexOutOfBoundsException();
if (length == 0)
return;
write0(buffer, offset, offset + length);
}
@Override
public void flush() throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
flush0();
}
@Override
public void close() throws IOException {
if (closed)
return;
closed = true;
close0();
}
private void write0(final byte[] buffer, final int from, final int to)
throws IOException {
for (int i = from; i < to; i++) {
data = (data << 8) | (buffer[i] & 0xff);
if (++modulus == 3) {
modulus = 0;
// write line separator if necessary
if (lineLength > 0 && linePosition >= lineLength) {
// writeLineSeparator() inlined for performance reasons
linePosition = 0;
if (encoded.length - position < lineSeparator.length)
flush0();
for (byte ls : lineSeparator)
encoded[position++] = ls;
}
// encode data into 4 bytes
if (encoded.length - position < 4)
flush0();
encoded[position++] = BASE64_TABLE[(data >> 18) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data >> 12) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data >> 6) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[data & MASK_6BITS];
linePosition += 4;
}
}
}
private void flush0() throws IOException {
if (position > 0) {
out.write(encoded, 0, position);
position = 0;
}
}
private void close0() throws IOException {
if (modulus != 0)
writePad();
// write line separator at the end of the encoded data
if (lineLength > 0 && linePosition > 0) {
writeLineSeparator();
}
flush0();
}
private void writePad() throws IOException {
// write line separator if necessary
if (lineLength > 0 && linePosition >= lineLength) {
writeLineSeparator();
}
// encode data into 4 bytes
if (encoded.length - position < 4)
flush0();
if (modulus == 1) {
encoded[position++] = BASE64_TABLE[(data >> 2) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data << 4) & MASK_6BITS];
encoded[position++] = BASE64_PAD;
encoded[position++] = BASE64_PAD;
} else {
assert modulus == 2;
encoded[position++] = BASE64_TABLE[(data >> 10) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data >> 4) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data << 2) & MASK_6BITS];
encoded[position++] = BASE64_PAD;
}
linePosition += 4;
}
private void writeLineSeparator() throws IOException {
linePosition = 0;
if (encoded.length - position < lineSeparator.length)
flush0();
for (byte ls : lineSeparator)
encoded[position++] = ls;
}
private void checkLineSeparator(byte[] lineSeparator) {
if (lineSeparator.length > ENCODED_BUFFER_SIZE)
throw new IllegalArgumentException("line separator length exceeds "
+ ENCODED_BUFFER_SIZE);
for (byte b : lineSeparator) {
if (BASE64_CHARS.contains(b)) {
throw new IllegalArgumentException(
"line separator must not contain base64 character '"
+ (char) (b & 0xff) + "'");
}
}
}
}