/******************************************************************************
*
* Copyright 2011-2012 Tavendo GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.magnum.soda.transport.wamp;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
/**
* OutputStream wrapping a ByteBuffer. This class internally allocates a
* direct ByteBuffer for use i.e. with NIO for socket I/O. The ByteBuffer
* is automatically enlarged if needed (preserving contents when enlarged).
*/
public class ByteBufferOutputStream extends OutputStream {
/// Initial size of allocated ByteBuffer.
private final int mInitialSize;
/// Amount to grow when ByteBuffer needs to be enlarged.
private final int mGrowSize;
/// Internal ByteBuffer wrapped.
private ByteBuffer mBuffer;
/**
* Create a direct allocated ByteBuffer wrapped as OutputStream.
*/
public ByteBufferOutputStream() {
this(2 * 65536, 65536);
}
/**
* Create a direct allocated ByteBuffer wrapped as OutputStream.
*
* @param initialSize Initial size of ByteBuffer.
* @param growSize When buffer needs to grow, enlarge by this amount.
*/
public ByteBufferOutputStream(int initialSize, int growSize) {
mInitialSize = initialSize;
mGrowSize = growSize;
mBuffer = ByteBuffer.allocateDirect(mInitialSize);
mBuffer.clear();
}
/**
* Get the underlying ByteBuffer.
*
* @return ByteBuffer underlying this OutputStream.
*/
public ByteBuffer getBuffer() {
return mBuffer;
}
/**
* Calls flip on the underyling ByteBuffer.
*/
public Buffer flip() {
return mBuffer.flip();
}
/**
* Calls clear on the underlying ByteBuffer.
*/
public Buffer clear() {
return mBuffer.clear();
}
/**
* Calls remaining() on underlying ByteBuffer.
*/
public int remaining() {
return mBuffer.remaining();
}
/**
* Expand the underlying ByteBuffer and preserve content.
*
* @param requestSize Requested new size.
*/
public synchronized void expand(int requestSize) {
if (requestSize > mBuffer.capacity()) {
ByteBuffer oldBuffer = mBuffer;
int oldPosition = mBuffer.position();
int newCapacity = ((requestSize / mGrowSize) + 1) * mGrowSize;
mBuffer = ByteBuffer.allocateDirect(newCapacity);
oldBuffer.clear();
mBuffer.clear();
mBuffer.put(oldBuffer);
mBuffer.position(oldPosition);
}
}
/**
* Write one byte to the underlying ByteBuffer via this OutputStream.
*
* @param b Byte to be written.
*/
@Override
public synchronized void write(int b) throws IOException {
if (mBuffer.position() + 1 > mBuffer.capacity()) {
expand(mBuffer.capacity() + 1);
}
mBuffer.put((byte) b);
}
/**
* Write a chunk of bytes to the underyling ByteBuffer via this
* OutputStream.
*
* @param bytes Write bytes from this byte array.
* @param off Start reading at this offset within byte array.
* @param len Write this many bytes, and enlarge underyling
* ByteBuffer when necessary, preserving the contents.
*/
@Override
public synchronized void write(byte[] bytes, int off, int len)
throws IOException {
if (mBuffer.position() + len > mBuffer.capacity()) {
expand(mBuffer.capacity() + len);
}
mBuffer.put(bytes, off, len);
}
/**
* Write a complete byte array to the underlying ByteBuffer via this
* OutputStream.
*
* @param bytes Byte array to be written.
*/
public synchronized void write(byte[] bytes) throws IOException {
write(bytes, 0, bytes.length);
}
/**
* Write the UTF-8 encoding of a String to the underlying ByteBuffer
* via this OutputStream.
*
* @param str String to be written.
* @throws IOException
*/
public synchronized void write(String str) throws IOException {
write(str.getBytes("UTF-8"));
}
/**
* Write CR-LF.
*
* @throws IOException
*/
public synchronized void crlf() throws IOException {
write(0x0d);
write(0x0a);
}
}