/* * 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. * */ package com.google.code.hs4j.network.buffer; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.OutputStream; import java.io.StreamCorruptedException; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.util.EnumSet; import java.util.Set; /** * A base implementation of {@link IoBuffer}. This implementation assumes that * {@link IoBuffer#buf()} always returns a correct NIO {@link ByteBuffer} * instance. Most implementations could extend this class and implement their * own buffer management mechanism. * * @author The Apache MINA Project (dev@mina.apache.org) * @version $Rev: 748210 $, $Date: 2009-02-26 18:05:40 +0100 (Thu, 26 Feb 2009) * $ * @see IoBufferAllocator */ public abstract class AbstractIoBuffer extends IoBuffer { /** Tells if a buffer has been created from an existing buffer */ private final boolean derived; /** A flag set to true if the buffer can extend automatically */ private boolean autoExpand; /** A flag set to true if the buffer can shrink automatically */ private boolean autoShrink; /** Tells if a buffer can be expanded */ private boolean recapacityAllowed = true; /** The minimum number of bytes the IoBuffer can hold */ private int minimumCapacity; /** A mask for a byte */ private static final long BYTE_MASK = 0xFFL; /** A mask for a short */ private static final long SHORT_MASK = 0xFFFFL; /** A mask for an int */ private static final long INT_MASK = 0xFFFFFFFFL; /** * We don't have any access to Buffer.markValue(), so we need to track it * down, which will cause small extra overhead. */ private int mark = -1; /** * Creates a new parent buffer. * * @param allocator * The allocator to use to create new buffers * @param initialCapacity * The initial buffer capacity when created */ protected AbstractIoBuffer(IoBufferAllocator allocator, int initialCapacity) { setAllocator(allocator); this.recapacityAllowed = true; this.derived = false; this.minimumCapacity = initialCapacity; } /** * Creates a new derived buffer. A derived buffer uses an existing buffer * properties - the allocator and capacity -. * * @param parent * The buffer we get the properties from */ protected AbstractIoBuffer(AbstractIoBuffer parent) { setAllocator(parent.getAllocator()); this.recapacityAllowed = false; this.derived = true; this.minimumCapacity = parent.minimumCapacity; } /** * {@inheritDoc} */ @Override public final boolean isDirect() { return buf().isDirect(); } /** * {@inheritDoc} */ @Override public final boolean isReadOnly() { return buf().isReadOnly(); } /** * Sets the underlying NIO buffer instance. * * @param newBuf * The buffer to store within this IoBuffer */ protected abstract void buf(ByteBuffer newBuf); /** * {@inheritDoc} */ @Override public final int minimumCapacity() { return minimumCapacity; } /** * {@inheritDoc} */ @Override public final IoBuffer minimumCapacity(int minimumCapacity) { if (minimumCapacity < 0) { throw new IllegalArgumentException("minimumCapacity: " + minimumCapacity); } this.minimumCapacity = minimumCapacity; return this; } /** * {@inheritDoc} */ @Override public final int capacity() { return buf().capacity(); } /** * {@inheritDoc} */ @Override public final IoBuffer capacity(int newCapacity) { if (!recapacityAllowed) { throw new IllegalStateException("Derived buffers and their parent can't be expanded."); } // Allocate a new buffer and transfer all settings to it. if (newCapacity > capacity()) { // Expand: // // Save the state. int pos = position(); int limit = limit(); ByteOrder bo = order(); // // Reallocate. ByteBuffer oldBuf = buf(); ByteBuffer newBuf = getAllocator().allocateNioBuffer(newCapacity, isDirect()); oldBuf.clear(); newBuf.put(oldBuf); buf(newBuf); // // Restore the state. buf().limit(limit); if (mark >= 0) { buf().position(mark); buf().mark(); } buf().position(pos); buf().order(bo); } return this; } /** * {@inheritDoc} */ @Override public final boolean isAutoExpand() { return autoExpand && recapacityAllowed; } /** * {@inheritDoc} */ @Override public final boolean isAutoShrink() { return autoShrink && recapacityAllowed; } /** * {@inheritDoc} */ @Override public final boolean isDerived() { return derived; } /** * {@inheritDoc} */ @Override public final IoBuffer setAutoExpand(boolean autoExpand) { if (!recapacityAllowed) { throw new IllegalStateException("Derived buffers and their parent can't be expanded."); } this.autoExpand = autoExpand; return this; } /** * {@inheritDoc} */ @Override public final IoBuffer setAutoShrink(boolean autoShrink) { if (!recapacityAllowed) { throw new IllegalStateException("Derived buffers and their parent can't be shrinked."); } this.autoShrink = autoShrink; return this; } /** * {@inheritDoc} */ @Override public final IoBuffer expand(int expectedRemaining) { return expand(position(), expectedRemaining, false); } private IoBuffer expand(int expectedRemaining, boolean autoExpand) { return expand(position(), expectedRemaining, autoExpand); } /** * {@inheritDoc} */ @Override public final IoBuffer expand(int pos, int expectedRemaining) { return expand(pos, expectedRemaining, false); } private IoBuffer expand(int pos, int expectedRemaining, boolean autoExpand) { if (!recapacityAllowed) { throw new IllegalStateException("Derived buffers and their parent can't be expanded."); } int end = pos + expectedRemaining; int newCapacity; if (autoExpand) { newCapacity = IoBuffer.normalizeCapacity(end); } else { newCapacity = end; } if (newCapacity > capacity()) { // The buffer needs expansion. capacity(newCapacity); } if (end > limit()) { // We call limit() directly to prevent StackOverflowError buf().limit(end); } return this; } /** * {@inheritDoc} */ @Override public final IoBuffer shrink() { if (!recapacityAllowed) { throw new IllegalStateException("Derived buffers and their parent can't be expanded."); } int position = position(); int capacity = capacity(); int limit = limit(); if (capacity == limit) { return this; } int newCapacity = capacity; int minCapacity = Math.max(minimumCapacity, limit); for (;;) { if (newCapacity >>> 1 < minCapacity) { break; } newCapacity >>>= 1; } newCapacity = Math.max(minCapacity, newCapacity); if (newCapacity == capacity) { return this; } // Shrink and compact: // // Save the state. ByteOrder bo = order(); // // Reallocate. ByteBuffer oldBuf = buf(); ByteBuffer newBuf = getAllocator().allocateNioBuffer(newCapacity, isDirect()); oldBuf.position(0); oldBuf.limit(limit); newBuf.put(oldBuf); buf(newBuf); // // Restore the state. buf().position(position); buf().limit(limit); buf().order(bo); mark = -1; return this; } /** * {@inheritDoc} */ @Override public final int position() { return buf().position(); } /** * {@inheritDoc} */ @Override public final IoBuffer position(int newPosition) { autoExpand(newPosition, 0); buf().position(newPosition); if (mark > newPosition) { mark = -1; } return this; } /** * {@inheritDoc} */ @Override public final int limit() { return buf().limit(); } /** * {@inheritDoc} */ @Override public final IoBuffer limit(int newLimit) { autoExpand(newLimit, 0); buf().limit(newLimit); if (mark > newLimit) { mark = -1; } return this; } /** * {@inheritDoc} */ @Override public final IoBuffer mark() { buf().mark(); mark = position(); return this; } /** * {@inheritDoc} */ @Override public final int markValue() { return mark; } /** * {@inheritDoc} */ @Override public final IoBuffer reset() { buf().reset(); return this; } /** * {@inheritDoc} */ @Override public final IoBuffer clear() { buf().clear(); mark = -1; return this; } /** * {@inheritDoc} */ @Override public final IoBuffer sweep() { clear(); return fillAndReset(remaining()); } /** * {@inheritDoc} */ @Override public final IoBuffer sweep(byte value) { clear(); return fillAndReset(value, remaining()); } /** * {@inheritDoc} */ @Override public final IoBuffer flip() { buf().flip(); mark = -1; return this; } /** * {@inheritDoc} */ @Override public final IoBuffer rewind() { buf().rewind(); mark = -1; return this; } /** * {@inheritDoc} */ @Override public final int remaining() { return limit() - position(); } /** * {@inheritDoc} */ @Override public final boolean hasRemaining() { return limit() > position(); } /** * {@inheritDoc} */ @Override public final byte get() { return buf().get(); } /** * {@inheritDoc} */ @Override public final short getUnsigned() { return (short) (get() & 0xff); } /** * {@inheritDoc} */ @Override public final IoBuffer put(byte b) { autoExpand(1); buf().put(b); return this; } /** * {@inheritDoc} */ @Override public final byte get(int index) { return buf().get(index); } /** * {@inheritDoc} */ @Override public final short getUnsigned(int index) { return (short) (get(index) & 0xff); } /** * {@inheritDoc} */ @Override public final IoBuffer put(int index, byte b) { autoExpand(index, 1); buf().put(index, b); return this; } /** * {@inheritDoc} */ @Override public final IoBuffer get(byte[] dst, int offset, int length) { buf().get(dst, offset, length); return this; } /** * {@inheritDoc} */ @Override public final IoBuffer put(ByteBuffer src) { autoExpand(src.remaining()); buf().put(src); return this; } /** * {@inheritDoc} */ @Override public final IoBuffer put(byte[] src, int offset, int length) { autoExpand(length); buf().put(src, offset, length); return this; } /** * {@inheritDoc} */ @Override public final IoBuffer compact() { int remaining = remaining(); int capacity = capacity(); if (capacity == 0) { return this; } if (isAutoShrink() && remaining <= capacity >>> 2 && capacity > minimumCapacity) { int newCapacity = capacity; int minCapacity = Math.max(minimumCapacity, remaining << 1); for (;;) { if (newCapacity >>> 1 < minCapacity) { break; } newCapacity >>>= 1; } newCapacity = Math.max(minCapacity, newCapacity); if (newCapacity == capacity) { return this; } // Shrink and compact: // // Save the state. ByteOrder bo = order(); // // Sanity check. if (remaining > newCapacity) { throw new IllegalStateException("The amount of the remaining bytes is greater than " + "the new capacity."); } // // Reallocate. ByteBuffer oldBuf = buf(); ByteBuffer newBuf = getAllocator().allocateNioBuffer(newCapacity, isDirect()); newBuf.put(oldBuf); buf(newBuf); // // Restore the state. buf().order(bo); } else { buf().compact(); } mark = -1; return this; } /** * {@inheritDoc} */ @Override public final ByteOrder order() { return buf().order(); } /** * {@inheritDoc} */ @Override public final IoBuffer order(ByteOrder bo) { buf().order(bo); return this; } /** * {@inheritDoc} */ @Override public final char getChar() { return buf().getChar(); } /** * {@inheritDoc} */ @Override public final IoBuffer putChar(char value) { autoExpand(2); buf().putChar(value); return this; } /** * {@inheritDoc} */ @Override public final char getChar(int index) { return buf().getChar(index); } /** * {@inheritDoc} */ @Override public final IoBuffer putChar(int index, char value) { autoExpand(index, 2); buf().putChar(index, value); return this; } /** * {@inheritDoc} */ @Override public final CharBuffer asCharBuffer() { return buf().asCharBuffer(); } /** * {@inheritDoc} */ @Override public final short getShort() { return buf().getShort(); } /** * {@inheritDoc} */ @Override public final IoBuffer putShort(short value) { autoExpand(2); buf().putShort(value); return this; } /** * {@inheritDoc} */ @Override public final short getShort(int index) { return buf().getShort(index); } /** * {@inheritDoc} */ @Override public final IoBuffer putShort(int index, short value) { autoExpand(index, 2); buf().putShort(index, value); return this; } /** * {@inheritDoc} */ @Override public final ShortBuffer asShortBuffer() { return buf().asShortBuffer(); } /** * {@inheritDoc} */ @Override public final int getInt() { return buf().getInt(); } /** * {@inheritDoc} */ @Override public final IoBuffer putInt(int value) { autoExpand(4); buf().putInt(value); return this; } /** * {@inheritDoc} */ @Override public final int getInt(int index) { return buf().getInt(index); } /** * {@inheritDoc} */ @Override public final IoBuffer putInt(int index, int value) { autoExpand(index, 4); buf().putInt(index, value); return this; } /** * {@inheritDoc} */ @Override public final IntBuffer asIntBuffer() { return buf().asIntBuffer(); } /** * {@inheritDoc} */ @Override public final long getLong() { return buf().getLong(); } /** * {@inheritDoc} */ @Override public final IoBuffer putLong(long value) { autoExpand(8); buf().putLong(value); return this; } /** * {@inheritDoc} */ @Override public final long getLong(int index) { return buf().getLong(index); } /** * {@inheritDoc} */ @Override public final IoBuffer putLong(int index, long value) { autoExpand(index, 8); buf().putLong(index, value); return this; } /** * {@inheritDoc} */ @Override public final LongBuffer asLongBuffer() { return buf().asLongBuffer(); } /** * {@inheritDoc} */ @Override public final float getFloat() { return buf().getFloat(); } /** * {@inheritDoc} */ @Override public final IoBuffer putFloat(float value) { autoExpand(4); buf().putFloat(value); return this; } /** * {@inheritDoc} */ @Override public final float getFloat(int index) { return buf().getFloat(index); } /** * {@inheritDoc} */ @Override public final IoBuffer putFloat(int index, float value) { autoExpand(index, 4); buf().putFloat(index, value); return this; } /** * {@inheritDoc} */ @Override public final FloatBuffer asFloatBuffer() { return buf().asFloatBuffer(); } /** * {@inheritDoc} */ @Override public final double getDouble() { return buf().getDouble(); } /** * {@inheritDoc} */ @Override public final IoBuffer putDouble(double value) { autoExpand(8); buf().putDouble(value); return this; } /** * {@inheritDoc} */ @Override public final double getDouble(int index) { return buf().getDouble(index); } /** * {@inheritDoc} */ @Override public final IoBuffer putDouble(int index, double value) { autoExpand(index, 8); buf().putDouble(index, value); return this; } /** * {@inheritDoc} */ @Override public final DoubleBuffer asDoubleBuffer() { return buf().asDoubleBuffer(); } /** * {@inheritDoc} */ @Override public final IoBuffer asReadOnlyBuffer() { recapacityAllowed = false; return asReadOnlyBuffer0(); } /** * Implement this method to return the unexpandable read only version of * this buffer. */ protected abstract IoBuffer asReadOnlyBuffer0(); /** * {@inheritDoc} */ @Override public final IoBuffer duplicate() { recapacityAllowed = false; return duplicate0(); } /** * Implement this method to return the unexpandable duplicate of this * buffer. */ protected abstract IoBuffer duplicate0(); /** * {@inheritDoc} */ @Override public final IoBuffer slice() { recapacityAllowed = false; return slice0(); } /** * {@inheritDoc} */ @Override public final IoBuffer getSlice(int index, int length) { if (length < 0) { throw new IllegalArgumentException("length: " + length); } int limit = limit(); if (index > limit) { throw new IllegalArgumentException("index: " + index); } int endIndex = index + length; if (capacity() < endIndex) { throw new IndexOutOfBoundsException("index + length (" + endIndex + ") is greater " + "than capacity (" + capacity() + ")."); } clear(); position(index); limit(endIndex); IoBuffer slice = slice(); position(index); limit(limit); return slice; } /** * {@inheritDoc} */ @Override public final IoBuffer getSlice(int length) { if (length < 0) { throw new IllegalArgumentException("length: " + length); } int pos = position(); int limit = limit(); int nextPos = pos + length; if (limit < nextPos) { throw new IndexOutOfBoundsException("position + length (" + nextPos + ") is greater " + "than limit (" + limit + ")."); } limit(pos + length); IoBuffer slice = slice(); position(nextPos); limit(limit); return slice; } /** * Implement this method to return the unexpandable slice of this buffer. */ protected abstract IoBuffer slice0(); /** * {@inheritDoc} */ @Override public int hashCode() { int h = 1; int p = position(); for (int i = limit() - 1; i >= p; i--) { h = 31 * h + get(i); } return h; } /** * {@inheritDoc} */ @Override public boolean equals(Object o) { if (!(o instanceof IoBuffer)) { return false; } IoBuffer that = (IoBuffer) o; if (this.remaining() != that.remaining()) { return false; } int p = this.position(); for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--) { byte v1 = this.get(i); byte v2 = that.get(j); if (v1 != v2) { return false; } } return true; } /** * {@inheritDoc} */ public int compareTo(IoBuffer that) { int n = this.position() + Math.min(this.remaining(), that.remaining()); for (int i = this.position(), j = that.position(); i < n; i++, j++) { byte v1 = this.get(i); byte v2 = that.get(j); if (v1 == v2) { continue; } if (v1 < v2) { return -1; } return +1; } return this.remaining() - that.remaining(); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder buf = new StringBuilder(); if (isDirect()) { buf.append("DirectBuffer"); } else { buf.append("HeapBuffer"); } buf.append("[pos="); buf.append(position()); buf.append(" lim="); buf.append(limit()); buf.append(" cap="); buf.append(capacity()); buf.append(": "); buf.append(getHexDump(16)); buf.append(']'); return buf.toString(); } /** * {@inheritDoc} */ @Override public IoBuffer get(byte[] dst) { return get(dst, 0, dst.length); } /** * {@inheritDoc} */ @Override public IoBuffer put(IoBuffer src) { return put(src.buf()); } /** * {@inheritDoc} */ @Override public IoBuffer put(byte[] src) { return put(src, 0, src.length); } /** * {@inheritDoc} */ @Override public int getUnsignedShort() { return getShort() & 0xffff; } /** * {@inheritDoc} */ @Override public int getUnsignedShort(int index) { return getShort(index) & 0xffff; } /** * {@inheritDoc} */ @Override public long getUnsignedInt() { return getInt() & 0xffffffffL; } /** * {@inheritDoc} */ @Override public int getMediumInt() { byte b1 = get(); byte b2 = get(); byte b3 = get(); if (ByteOrder.BIG_ENDIAN.equals(order())) { return getMediumInt(b1, b2, b3); } else { return getMediumInt(b3, b2, b1); } } /** * {@inheritDoc} */ @Override public int getUnsignedMediumInt() { int b1 = getUnsigned(); int b2 = getUnsigned(); int b3 = getUnsigned(); if (ByteOrder.BIG_ENDIAN.equals(order())) { return b1 << 16 | b2 << 8 | b3; } else { return b3 << 16 | b2 << 8 | b1; } } /** * {@inheritDoc} */ @Override public int getMediumInt(int index) { byte b1 = get(index); byte b2 = get(index + 1); byte b3 = get(index + 2); if (ByteOrder.BIG_ENDIAN.equals(order())) { return getMediumInt(b1, b2, b3); } else { return getMediumInt(b3, b2, b1); } } /** * {@inheritDoc} */ @Override public int getUnsignedMediumInt(int index) { int b1 = getUnsigned(index); int b2 = getUnsigned(index + 1); int b3 = getUnsigned(index + 2); if (ByteOrder.BIG_ENDIAN.equals(order())) { return b1 << 16 | b2 << 8 | b3; } else { return b3 << 16 | b2 << 8 | b1; } } /** * {@inheritDoc} */ private int getMediumInt(byte b1, byte b2, byte b3) { int ret = b1 << 16 & 0xff0000 | b2 << 8 & 0xff00 | b3 & 0xff; // Check to see if the medium int is negative (high bit in b1 set) if ((b1 & 0x80) == 0x80) { // Make the the whole int negative ret |= 0xff000000; } return ret; } /** * {@inheritDoc} */ @Override public IoBuffer putMediumInt(int value) { byte b1 = (byte) (value >> 16); byte b2 = (byte) (value >> 8); byte b3 = (byte) value; if (ByteOrder.BIG_ENDIAN.equals(order())) { put(b1).put(b2).put(b3); } else { put(b3).put(b2).put(b1); } return this; } /** * {@inheritDoc} */ @Override public IoBuffer putMediumInt(int index, int value) { byte b1 = (byte) (value >> 16); byte b2 = (byte) (value >> 8); byte b3 = (byte) value; if (ByteOrder.BIG_ENDIAN.equals(order())) { put(index, b1).put(index + 1, b2).put(index + 2, b3); } else { put(index, b3).put(index + 1, b2).put(index + 2, b1); } return this; } /** * {@inheritDoc} */ @Override public long getUnsignedInt(int index) { return getInt(index) & 0xffffffffL; } /** * {@inheritDoc} */ @Override public InputStream asInputStream() { return new InputStream() { @Override public int available() { return AbstractIoBuffer.this.remaining(); } @Override public synchronized void mark(int readlimit) { AbstractIoBuffer.this.mark(); } @Override public boolean markSupported() { return true; } @Override public int read() { if (AbstractIoBuffer.this.hasRemaining()) { return AbstractIoBuffer.this.get() & 0xff; } else { return -1; } } @Override public int read(byte[] b, int off, int len) { int remaining = AbstractIoBuffer.this.remaining(); if (remaining > 0) { int readBytes = Math.min(remaining, len); AbstractIoBuffer.this.get(b, off, readBytes); return readBytes; } else { return -1; } } @Override public synchronized void reset() { AbstractIoBuffer.this.reset(); } @Override public long skip(long n) { int bytes; if (n > Integer.MAX_VALUE) { bytes = AbstractIoBuffer.this.remaining(); } else { bytes = Math.min(AbstractIoBuffer.this.remaining(), (int) n); } AbstractIoBuffer.this.skip(bytes); return bytes; } }; } /** * {@inheritDoc} */ @Override public OutputStream asOutputStream() { return new OutputStream() { @Override public void write(byte[] b, int off, int len) { AbstractIoBuffer.this.put(b, off, len); } @Override public void write(int b) { AbstractIoBuffer.this.put((byte) b); } }; } /** * {@inheritDoc} */ @Override public String getHexDump() { return this.getHexDump(Integer.MAX_VALUE); } /** * {@inheritDoc} */ @Override public String getHexDump(int lengthLimit) { return IoBufferHexDumper.getHexdump(this, lengthLimit); } /** * {@inheritDoc} */ @Override public String getString(CharsetDecoder decoder) throws CharacterCodingException { if (!hasRemaining()) { return ""; } boolean utf16 = decoder.charset().name().startsWith("UTF-16"); int oldPos = position(); int oldLimit = limit(); int end = -1; int newPos; if (!utf16) { end = indexOf((byte) 0x00); if (end < 0) { newPos = end = oldLimit; } else { newPos = end + 1; } } else { int i = oldPos; for (;;) { boolean wasZero = get(i) == 0; i++; if (i >= oldLimit) { break; } if (get(i) != 0) { i++; if (i >= oldLimit) { break; } else { continue; } } if (wasZero) { end = i - 1; break; } } if (end < 0) { newPos = end = oldPos + (oldLimit - oldPos & 0xFFFFFFFE); } else { if (end + 2 <= oldLimit) { newPos = end + 2; } else { newPos = end; } } } if (oldPos == end) { position(newPos); return ""; } limit(end); decoder.reset(); int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1; CharBuffer out = CharBuffer.allocate(expectedLength); for (;;) { CoderResult cr; if (hasRemaining()) { cr = decoder.decode(buf(), out, true); } else { cr = decoder.flush(out); } if (cr.isUnderflow()) { break; } if (cr.isOverflow()) { CharBuffer o = CharBuffer.allocate(out.capacity() + expectedLength); out.flip(); o.put(out); out = o; continue; } if (cr.isError()) { // Revert the buffer back to the previous state. limit(oldLimit); position(oldPos); cr.throwException(); } } limit(oldLimit); position(newPos); return out.flip().toString(); } /** * {@inheritDoc} */ @Override public String getString(int fieldSize, CharsetDecoder decoder) throws CharacterCodingException { checkFieldSize(fieldSize); if (fieldSize == 0) { return ""; } if (!hasRemaining()) { return ""; } boolean utf16 = decoder.charset().name().startsWith("UTF-16"); if (utf16 && (fieldSize & 1) != 0) { throw new IllegalArgumentException("fieldSize is not even."); } int oldPos = position(); int oldLimit = limit(); int end = oldPos + fieldSize; if (oldLimit < end) { throw new BufferUnderflowException(); } int i; if (!utf16) { for (i = oldPos; i < end; i++) { if (get(i) == 0) { break; } } if (i == end) { limit(end); } else { limit(i); } } else { for (i = oldPos; i < end; i += 2) { if (get(i) == 0 && get(i + 1) == 0) { break; } } if (i == end) { limit(end); } else { limit(i); } } if (!hasRemaining()) { limit(oldLimit); position(end); return ""; } decoder.reset(); int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1; CharBuffer out = CharBuffer.allocate(expectedLength); for (;;) { CoderResult cr; if (hasRemaining()) { cr = decoder.decode(buf(), out, true); } else { cr = decoder.flush(out); } if (cr.isUnderflow()) { break; } if (cr.isOverflow()) { CharBuffer o = CharBuffer.allocate(out.capacity() + expectedLength); out.flip(); o.put(out); out = o; continue; } if (cr.isError()) { // Revert the buffer back to the previous state. limit(oldLimit); position(oldPos); cr.throwException(); } } limit(oldLimit); position(end); return out.flip().toString(); } /** * {@inheritDoc} */ @Override public IoBuffer putString(CharSequence val, CharsetEncoder encoder) throws CharacterCodingException { if (val.length() == 0) { return this; } CharBuffer in = CharBuffer.wrap(val); encoder.reset(); int expandedState = 0; for (;;) { CoderResult cr; if (in.hasRemaining()) { cr = encoder.encode(in, buf(), true); } else { cr = encoder.flush(buf()); } if (cr.isUnderflow()) { break; } if (cr.isOverflow()) { if (isAutoExpand()) { switch (expandedState) { case 0: autoExpand((int) Math.ceil(in.remaining() * encoder.averageBytesPerChar())); expandedState++; break; case 1: autoExpand((int) Math.ceil(in.remaining() * encoder.maxBytesPerChar())); expandedState++; break; default: throw new RuntimeException("Expanded by " + (int) Math.ceil(in.remaining() * encoder.maxBytesPerChar()) + " but that wasn't enough for '" + val + "'"); } continue; } } else { expandedState = 0; } cr.throwException(); } return this; } /** * {@inheritDoc} */ @Override public IoBuffer putString(CharSequence val, int fieldSize, CharsetEncoder encoder) throws CharacterCodingException { checkFieldSize(fieldSize); if (fieldSize == 0) { return this; } autoExpand(fieldSize); boolean utf16 = encoder.charset().name().startsWith("UTF-16"); if (utf16 && (fieldSize & 1) != 0) { throw new IllegalArgumentException("fieldSize is not even."); } int oldLimit = limit(); int end = position() + fieldSize; if (oldLimit < end) { throw new BufferOverflowException(); } if (val.length() == 0) { if (!utf16) { put((byte) 0x00); } else { put((byte) 0x00); put((byte) 0x00); } position(end); return this; } CharBuffer in = CharBuffer.wrap(val); limit(end); encoder.reset(); for (;;) { CoderResult cr; if (in.hasRemaining()) { cr = encoder.encode(in, buf(), true); } else { cr = encoder.flush(buf()); } if (cr.isUnderflow() || cr.isOverflow()) { break; } cr.throwException(); } limit(oldLimit); if (position() < end) { if (!utf16) { put((byte) 0x00); } else { put((byte) 0x00); put((byte) 0x00); } } position(end); return this; } /** * {@inheritDoc} */ @Override public String getPrefixedString(CharsetDecoder decoder) throws CharacterCodingException { return getPrefixedString(2, decoder); } /** * Reads a string which has a length field before the actual encoded string, * using the specified <code>decoder</code> and returns it. * * @param prefixLength * the length of the length field (1, 2, or 4) * @param decoder * the decoder to use for decoding the string * @return the prefixed string * @throws CharacterCodingException * when decoding fails * @throws BufferUnderflowException * when there is not enough data available */ @Override public String getPrefixedString(int prefixLength, CharsetDecoder decoder) throws CharacterCodingException { if (!prefixedDataAvailable(prefixLength)) { throw new BufferUnderflowException(); } int fieldSize = 0; switch (prefixLength) { case 1: fieldSize = getUnsigned(); break; case 2: fieldSize = getUnsignedShort(); break; case 4: fieldSize = getInt(); break; } if (fieldSize == 0) { return ""; } boolean utf16 = decoder.charset().name().startsWith("UTF-16"); if (utf16 && (fieldSize & 1) != 0) { throw new BufferDataException("fieldSize is not even for a UTF-16 string."); } int oldLimit = limit(); int end = position() + fieldSize; if (oldLimit < end) { throw new BufferUnderflowException(); } limit(end); decoder.reset(); int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1; CharBuffer out = CharBuffer.allocate(expectedLength); for (;;) { CoderResult cr; if (hasRemaining()) { cr = decoder.decode(buf(), out, true); } else { cr = decoder.flush(out); } if (cr.isUnderflow()) { break; } if (cr.isOverflow()) { CharBuffer o = CharBuffer.allocate(out.capacity() + expectedLength); out.flip(); o.put(out); out = o; continue; } cr.throwException(); } limit(oldLimit); position(end); return out.flip().toString(); } /** * {@inheritDoc} */ @Override public IoBuffer putPrefixedString(CharSequence in, CharsetEncoder encoder) throws CharacterCodingException { return putPrefixedString(in, 2, 0, encoder); } /** * {@inheritDoc} */ @Override public IoBuffer putPrefixedString(CharSequence in, int prefixLength, CharsetEncoder encoder) throws CharacterCodingException { return putPrefixedString(in, prefixLength, 0, encoder); } /** * {@inheritDoc} */ @Override public IoBuffer putPrefixedString(CharSequence in, int prefixLength, int padding, CharsetEncoder encoder) throws CharacterCodingException { return putPrefixedString(in, prefixLength, padding, (byte) 0, encoder); } /** * {@inheritDoc} */ @Override public IoBuffer putPrefixedString(CharSequence val, int prefixLength, int padding, byte padValue, CharsetEncoder encoder) throws CharacterCodingException { int maxLength; switch (prefixLength) { case 1: maxLength = 255; break; case 2: maxLength = 65535; break; case 4: maxLength = Integer.MAX_VALUE; break; default: throw new IllegalArgumentException("prefixLength: " + prefixLength); } if (val.length() > maxLength) { throw new IllegalArgumentException("The specified string is too long."); } if (val.length() == 0) { switch (prefixLength) { case 1: put((byte) 0); break; case 2: putShort((short) 0); break; case 4: putInt(0); break; } return this; } int padMask; switch (padding) { case 0: case 1: padMask = 0; break; case 2: padMask = 1; break; case 4: padMask = 3; break; default: throw new IllegalArgumentException("padding: " + padding); } CharBuffer in = CharBuffer.wrap(val); skip(prefixLength); // make a room for the length field int oldPos = position(); encoder.reset(); int expandedState = 0; for (;;) { CoderResult cr; if (in.hasRemaining()) { cr = encoder.encode(in, buf(), true); } else { cr = encoder.flush(buf()); } if (position() - oldPos > maxLength) { throw new IllegalArgumentException("The specified string is too long."); } if (cr.isUnderflow()) { break; } if (cr.isOverflow()) { if (isAutoExpand()) { switch (expandedState) { case 0: autoExpand((int) Math.ceil(in.remaining() * encoder.averageBytesPerChar())); expandedState++; break; case 1: autoExpand((int) Math.ceil(in.remaining() * encoder.maxBytesPerChar())); expandedState++; break; default: throw new RuntimeException("Expanded by " + (int) Math.ceil(in.remaining() * encoder.maxBytesPerChar()) + " but that wasn't enough for '" + val + "'"); } continue; } } else { expandedState = 0; } cr.throwException(); } // Write the length field fill(padValue, padding - (position() - oldPos & padMask)); int length = position() - oldPos; switch (prefixLength) { case 1: put(oldPos - 1, (byte) length); break; case 2: putShort(oldPos - 2, (short) length); break; case 4: putInt(oldPos - 4, length); break; } return this; } /** * {@inheritDoc} */ @Override public Object getObject() throws ClassNotFoundException { return getObject(Thread.currentThread().getContextClassLoader()); } /** * {@inheritDoc} */ @Override public Object getObject(final ClassLoader classLoader) throws ClassNotFoundException { if (!prefixedDataAvailable(4)) { throw new BufferUnderflowException(); } int length = getInt(); if (length <= 4) { throw new BufferDataException("Object length should be greater than 4: " + length); } int oldLimit = limit(); limit(position() + length); try { ObjectInputStream in = new ObjectInputStream(asInputStream()) { @Override protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { int type = read(); if (type < 0) { throw new EOFException(); } switch (type) { case 0: // Primitive types return super.readClassDescriptor(); case 1: // Non-primitive types String className = readUTF(); Class<?> clazz = Class.forName(className, true, classLoader); return ObjectStreamClass.lookup(clazz); default: throw new StreamCorruptedException("Unexpected class descriptor type: " + type); } } @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name = desc.getName(); try { return Class.forName(name, false, classLoader); } catch (ClassNotFoundException ex) { return super.resolveClass(desc); } } }; return in.readObject(); } catch (IOException e) { throw new BufferDataException(e); } finally { limit(oldLimit); } } /** * {@inheritDoc} */ @Override public IoBuffer putObject(Object o) { int oldPos = position(); skip(4); // Make a room for the length field. try { ObjectOutputStream out = new ObjectOutputStream(asOutputStream()) { @Override protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { if (desc.forClass().isPrimitive()) { write(0); super.writeClassDescriptor(desc); } else { write(1); writeUTF(desc.getName()); } } }; out.writeObject(o); out.flush(); } catch (IOException e) { throw new BufferDataException(e); } // Fill the length field int newPos = position(); position(oldPos); putInt(newPos - oldPos - 4); position(newPos); return this; } /** * {@inheritDoc} */ @Override public boolean prefixedDataAvailable(int prefixLength) { return prefixedDataAvailable(prefixLength, Integer.MAX_VALUE); } /** * {@inheritDoc} */ @Override public boolean prefixedDataAvailable(int prefixLength, int maxDataLength) { if (remaining() < prefixLength) { return false; } int dataLength; switch (prefixLength) { case 1: dataLength = getUnsigned(position()); break; case 2: dataLength = getUnsignedShort(position()); break; case 4: dataLength = getInt(position()); break; default: throw new IllegalArgumentException("prefixLength: " + prefixLength); } if (dataLength < 0 || dataLength > maxDataLength) { throw new BufferDataException("dataLength: " + dataLength); } return remaining() - prefixLength >= dataLength; } /** * {@inheritDoc} */ @Override public int indexOf(byte b) { if (hasArray()) { int arrayOffset = arrayOffset(); int beginPos = arrayOffset + position(); int limit = arrayOffset + limit(); byte[] array = array(); for (int i = beginPos; i < limit; i++) { if (array[i] == b) { return i - arrayOffset; } } } else { int beginPos = position(); int limit = limit(); for (int i = beginPos; i < limit; i++) { if (get(i) == b) { return i; } } } return -1; } /** * {@inheritDoc} */ @Override public IoBuffer skip(int size) { autoExpand(size); return position(position() + size); } /** * {@inheritDoc} */ @Override public IoBuffer fill(byte value, int size) { autoExpand(size); int q = size >>> 3; int r = size & 7; if (q > 0) { int intValue = value | value << 8 | value << 16 | value << 24; long longValue = intValue; longValue <<= 32; longValue |= intValue; for (int i = q; i > 0; i--) { putLong(longValue); } } q = r >>> 2; r = r & 3; if (q > 0) { int intValue = value | value << 8 | value << 16 | value << 24; putInt(intValue); } q = r >> 1; r = r & 1; if (q > 0) { short shortValue = (short) (value | value << 8); putShort(shortValue); } if (r > 0) { put(value); } return this; } /** * {@inheritDoc} */ @Override public IoBuffer fillAndReset(byte value, int size) { autoExpand(size); int pos = position(); try { fill(value, size); } finally { position(pos); } return this; } /** * {@inheritDoc} */ @Override public IoBuffer fill(int size) { autoExpand(size); int q = size >>> 3; int r = size & 7; for (int i = q; i > 0; i--) { putLong(0L); } q = r >>> 2; r = r & 3; if (q > 0) { putInt(0); } q = r >> 1; r = r & 1; if (q > 0) { putShort((short) 0); } if (r > 0) { put((byte) 0); } return this; } /** * {@inheritDoc} */ @Override public IoBuffer fillAndReset(int size) { autoExpand(size); int pos = position(); try { fill(size); } finally { position(pos); } return this; } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> E getEnum(Class<E> enumClass) { return toEnum(enumClass, getUnsigned()); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> E getEnum(int index, Class<E> enumClass) { return toEnum(enumClass, getUnsigned(index)); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> E getEnumShort(Class<E> enumClass) { return toEnum(enumClass, getUnsignedShort()); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> E getEnumShort(int index, Class<E> enumClass) { return toEnum(enumClass, getUnsignedShort(index)); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> E getEnumInt(Class<E> enumClass) { return toEnum(enumClass, getInt()); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> E getEnumInt(int index, Class<E> enumClass) { return toEnum(enumClass, getInt(index)); } /** * {@inheritDoc} */ @Override public IoBuffer putEnum(Enum<?> e) { if (e.ordinal() > BYTE_MASK) { throw new IllegalArgumentException(enumConversionErrorMessage(e, "byte")); } return put((byte) e.ordinal()); } /** * {@inheritDoc} */ @Override public IoBuffer putEnum(int index, Enum<?> e) { if (e.ordinal() > BYTE_MASK) { throw new IllegalArgumentException(enumConversionErrorMessage(e, "byte")); } return put(index, (byte) e.ordinal()); } /** * {@inheritDoc} */ @Override public IoBuffer putEnumShort(Enum<?> e) { if (e.ordinal() > SHORT_MASK) { throw new IllegalArgumentException(enumConversionErrorMessage(e, "short")); } return putShort((short) e.ordinal()); } /** * {@inheritDoc} */ @Override public IoBuffer putEnumShort(int index, Enum<?> e) { if (e.ordinal() > SHORT_MASK) { throw new IllegalArgumentException(enumConversionErrorMessage(e, "short")); } return putShort(index, (short) e.ordinal()); } /** * {@inheritDoc} */ @Override public IoBuffer putEnumInt(Enum<?> e) { return putInt(e.ordinal()); } /** * {@inheritDoc} */ @Override public IoBuffer putEnumInt(int index, Enum<?> e) { return putInt(index, e.ordinal()); } private <E> E toEnum(Class<E> enumClass, int i) { E[] enumConstants = enumClass.getEnumConstants(); if (i > enumConstants.length) { throw new IndexOutOfBoundsException(String.format( "%d is too large of an ordinal to convert to the enum %s", i, enumClass.getName())); } return enumConstants[i]; } private String enumConversionErrorMessage(Enum<?> e, String type) { return String.format("%s.%s has an ordinal value too large for a %s", e.getClass().getName(), e.name(), type); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSet(Class<E> enumClass) { return toEnumSet(enumClass, get() & BYTE_MASK); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSet(int index, Class<E> enumClass) { return toEnumSet(enumClass, get(index) & BYTE_MASK); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSetShort(Class<E> enumClass) { return toEnumSet(enumClass, getShort() & SHORT_MASK); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSetShort(int index, Class<E> enumClass) { return toEnumSet(enumClass, getShort(index) & SHORT_MASK); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSetInt(Class<E> enumClass) { return toEnumSet(enumClass, getInt() & INT_MASK); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSetInt(int index, Class<E> enumClass) { return toEnumSet(enumClass, getInt(index) & INT_MASK); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSetLong(Class<E> enumClass) { return toEnumSet(enumClass, getLong()); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> EnumSet<E> getEnumSetLong(int index, Class<E> enumClass) { return toEnumSet(enumClass, getLong(index)); } private <E extends Enum<E>> EnumSet<E> toEnumSet(Class<E> clazz, long vector) { EnumSet<E> set = EnumSet.noneOf(clazz); long mask = 1; for (E e : clazz.getEnumConstants()) { if ((mask & vector) == mask) { set.add(e); } mask <<= 1; } return set; } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSet(Set<E> set) { long vector = toLong(set); if ((vector & ~BYTE_MASK) != 0) { throw new IllegalArgumentException("The enum set is too large to fit in a byte: " + set); } return put((byte) vector); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSet(int index, Set<E> set) { long vector = toLong(set); if ((vector & ~BYTE_MASK) != 0) { throw new IllegalArgumentException("The enum set is too large to fit in a byte: " + set); } return put(index, (byte) vector); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSetShort(Set<E> set) { long vector = toLong(set); if ((vector & ~SHORT_MASK) != 0) { throw new IllegalArgumentException("The enum set is too large to fit in a short: " + set); } return putShort((short) vector); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSetShort(int index, Set<E> set) { long vector = toLong(set); if ((vector & ~SHORT_MASK) != 0) { throw new IllegalArgumentException("The enum set is too large to fit in a short: " + set); } return putShort(index, (short) vector); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSetInt(Set<E> set) { long vector = toLong(set); if ((vector & ~INT_MASK) != 0) { throw new IllegalArgumentException("The enum set is too large to fit in an int: " + set); } return putInt((int) vector); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSetInt(int index, Set<E> set) { long vector = toLong(set); if ((vector & ~INT_MASK) != 0) { throw new IllegalArgumentException("The enum set is too large to fit in an int: " + set); } return putInt(index, (int) vector); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSetLong(Set<E> set) { return putLong(toLong(set)); } /** * {@inheritDoc} */ @Override public <E extends Enum<E>> IoBuffer putEnumSetLong(int index, Set<E> set) { return putLong(index, toLong(set)); } private <E extends Enum<E>> long toLong(Set<E> set) { long vector = 0; for (E e : set) { if (e.ordinal() >= Long.SIZE) { throw new IllegalArgumentException("The enum set is too large to fit in a bit vector: " + set); } vector |= 1L << e.ordinal(); } return vector; } /** * This method forwards the call to {@link #expand(int)} only when * <tt>autoExpand</tt> property is <tt>true</tt>. */ private IoBuffer autoExpand(int expectedRemaining) { if (isAutoExpand()) { expand(expectedRemaining, true); } return this; } /** * This method forwards the call to {@link #expand(int)} only when * <tt>autoExpand</tt> property is <tt>true</tt>. */ private IoBuffer autoExpand(int pos, int expectedRemaining) { if (isAutoExpand()) { expand(pos, expectedRemaining, true); } return this; } private static void checkFieldSize(int fieldSize) { if (fieldSize < 0) { throw new IllegalArgumentException("fieldSize cannot be negative: " + fieldSize); } } }