All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.code.yanf4j.buffer.AbstractIoBuffer Maven / Gradle / Ivy

There is a newer version: 2.4.8
Show newest version
/*
 * 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.yanf4j.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 ([email protected])
 * @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
   * decoder 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 getEnum(Class enumClass) {
    return toEnum(enumClass, getUnsigned());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > E getEnum(int index, Class enumClass) {
    return toEnum(enumClass, getUnsigned(index));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > E getEnumShort(Class enumClass) {
    return toEnum(enumClass, getUnsignedShort());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > E getEnumShort(int index, Class enumClass) {
    return toEnum(enumClass, getUnsignedShort(index));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > E getEnumInt(Class enumClass) {
    return toEnum(enumClass, getInt());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > E getEnumInt(int index, Class 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 toEnum(Class 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 > EnumSet getEnumSet(Class enumClass) {
    return toEnumSet(enumClass, get() & BYTE_MASK);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > EnumSet getEnumSet(int index, Class enumClass) {
    return toEnumSet(enumClass, get(index) & BYTE_MASK);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > EnumSet getEnumSetShort(Class enumClass) {
    return toEnumSet(enumClass, getShort() & SHORT_MASK);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > EnumSet getEnumSetShort(int index, Class enumClass) {
    return toEnumSet(enumClass, getShort(index) & SHORT_MASK);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > EnumSet getEnumSetInt(Class enumClass) {
    return toEnumSet(enumClass, getInt() & INT_MASK);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > EnumSet getEnumSetInt(int index, Class enumClass) {
    return toEnumSet(enumClass, getInt(index) & INT_MASK);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > EnumSet getEnumSetLong(Class enumClass) {
    return toEnumSet(enumClass, getLong());
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > EnumSet getEnumSetLong(int index, Class enumClass) {
    return toEnumSet(enumClass, getLong(index));
  }

  private > EnumSet toEnumSet(Class clazz, long vector) {
    EnumSet 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 > IoBuffer putEnumSet(Set 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 > IoBuffer putEnumSet(int index, Set 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 > IoBuffer putEnumSetShort(Set 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 > IoBuffer putEnumSetShort(int index, Set 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 > IoBuffer putEnumSetInt(Set 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 > IoBuffer putEnumSetInt(int index, Set 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 > IoBuffer putEnumSetLong(Set set) {
    return putLong(toLong(set));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public > IoBuffer putEnumSetLong(int index, Set set) {
    return putLong(index, toLong(set));
  }

  private > long toLong(Set 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 autoExpand property is
   * true.
   */
  private IoBuffer autoExpand(int expectedRemaining) {
    if (isAutoExpand()) {
      expand(expectedRemaining, true);
    }
    return this;
  }

  /**
   * This method forwards the call to {@link #expand(int)} only when autoExpand property is
   * true.
   */
  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);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy