/*
* 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 org.apache.mina.core.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: 706583 $, $Date: 2008-10-21 19:41:51 +0800 (星期二, 2008-10-21) $
* @see IoBufferAllocator
*/
public abstract class AbstractIoBuffer extends IoBuffer {
private final IoBufferAllocator allocator;
private final boolean derived;
private boolean autoExpand;
private boolean autoShrink;
private boolean recapacityAllowed = true;
private int minimumCapacity;
/**
* 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.
*/
protected AbstractIoBuffer(IoBufferAllocator allocator, int initialCapacity) {
this.allocator = allocator;
this.recapacityAllowed = true;
this.derived = false;
this.minimumCapacity = initialCapacity;
}
/**
* Creates a new derived buffer.
*/
protected AbstractIoBuffer(AbstractIoBuffer parent) {
this.allocator = parent.allocator;
this.recapacityAllowed = false;
this.derived = true;
this.minimumCapacity = parent.minimumCapacity;
}
@Override
public final boolean isDirect() {
return buf().isDirect();
}
@Override
public final boolean isReadOnly() {
return buf().isReadOnly();
}
/**
* Sets the underlying NIO buffer instance.
*/
protected abstract void buf(ByteBuffer newBuf);
@Override
public final int minimumCapacity() {
return minimumCapacity;
}
@Override
public final IoBuffer minimumCapacity(int minimumCapacity) {
if (minimumCapacity < 0) {
throw new IllegalArgumentException("minimumCapacity: "
+ minimumCapacity);
}
this.minimumCapacity = minimumCapacity;
return this;
}
@Override
public final int capacity() {
return buf().capacity();
}
@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 = allocator.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;
}
@Override
public final boolean isAutoExpand() {
return autoExpand && recapacityAllowed;
}
@Override
public final boolean isAutoShrink() {
return autoShrink && recapacityAllowed;
}
@Override
public final boolean isDerived() {
return derived;
}
@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;
}
@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;
}
@Override
public final IoBuffer expand(int expectedRemaining) {
return expand(position(), expectedRemaining, false);
}
private IoBuffer expand(int expectedRemaining, boolean autoExpand) {
return expand(position(), expectedRemaining, autoExpand);
}
@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;
}
@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();