package net.sf.cotta.memory;
import net.sf.cotta.ByteArrayIndexOutOfBoundsException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
/**
* Faster performing ByteArrayBuffer using the ByteArrayOutputStream as a
* backing up stream. Writes 400MB in 44 seconds. Requires approx 2x memory
* of the file size, eg for 400MB set -Xmx1024m.
* <p/>
* Contributed by:
* Sergey Abramov
* http://coldcore.com
*/
public class ByteArrayBuffer {
private int size = 0;
public static final int INCREMENT = 8192;
/**
* Flush the backing up stream into the byte array at
* every 10MB (optimal performance)
*/
private static final int FLUSH_AT = 1024 * 1024 * 10;
/**
* The byte buffer where the backing up stream flushes its data.
*/
private byte[] buffer;
/**
* All the data is written into this backing up stream, which is
* then flushed into the byte buffer.
*/
private ByteArrayOutputStream bout;
public ByteArrayBuffer(byte[] content) {
this(content, INCREMENT);
}
public ByteArrayBuffer(byte[] content, int increament) {
this.buffer = new byte[content.length];
System.arraycopy(content, 0, buffer, 0, content.length);
size = content.length;
bout = new ByteArrayOutputStream(increament);
}
public ByteArrayBuffer() {
this(INCREMENT);
}
public ByteArrayBuffer(int initialCapacity) {
this(initialCapacity, INCREMENT);
}
public ByteArrayBuffer(int initialCapacity, int increment) {
buffer = new byte[initialCapacity];
bout = new ByteArrayOutputStream(increment);
}
/**
* Flush data from the backing up stream into the byte buffer.
*/
private void flush() {
int bsize = bout.size();
if (bsize > 0) {
if (size > buffer.length) increaseCapacity(size);
System.arraycopy(bout.toByteArray(), 0, buffer, size - bsize, bsize);
bout.reset();
}
}
public byte[] toByteArray() {
flush();
byte[] result = new byte[size];
System.arraycopy(buffer, 0, result, 0, size);
return result;
}
public ByteArrayBuffer append(byte b) {
bout.write(b);
size++;
if (size % FLUSH_AT == 0) flush();
return this;
}
/**
* Faster performing method, output stream should delegate to it (comment above).
*/
public ByteArrayBuffer append(byte[] b, int off, int len) {
bout.write(b, off, len);
size += len;
if (size % FLUSH_AT == 0) flush();
return this;
}
/**
* Faster performing method, output stream should delegate to it (comment above).
*/
public ByteArrayBuffer append(byte[] b) {
return append(b, 0, b.length);
}
/**
* This works for the default charset only:
* <p/>
* String str = ""+(char)1077+(char)1078; //UTF-8
* System.out.println((int)str.charAt(0)); //1077
* str = new String(str.getBytes(), "UTF-8");
* System.out.println((int)str.charAt(0)); //63, no UTF-8 any longer
*/
public void append(String value) throws IOException {
byte[] bytes = value.getBytes();
append(bytes);
}
/**
* This one converts to the proper charset.
*/
public void append(String value, String charsetName) throws IOException {
byte[] bytes = value.getBytes(charsetName);
append(bytes);
}
private void increaseCapacity(int targetCapacity) {
byte[] newBuffer = new byte[targetCapacity];
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
buffer = newBuffer;
}
public byte byteAt(long position) {
return byteAt((int) position);
}
public byte byteAt(int position) {
if (position < 0 || position >= size) {
throw new ByteArrayIndexOutOfBoundsException(position, size);
}
flush();
return buffer[position];
}
public int size() {
return size;
}
public int copyFrom(ByteBuffer src) {
flush();
int sizeToCopy = src.remaining();
int resultingSize = sizeToCopy + size;
if (buffer.length < resultingSize) {
increaseCapacity(resultingSize);
}
src.get(buffer, size, sizeToCopy);
size += sizeToCopy;
return sizeToCopy;
}
public long copyTo(ByteBuffer dst, int start, int count) {
int end = Math.min(size, start + count);
int sizeToCopy = end - start;
if (sizeToCopy <= 0) {
return 0;
}
flush();
dst.put(buffer, start, sizeToCopy);
return sizeToCopy;
}
public long copyTo(WritableByteChannel target, long position, long count) throws IOException {
long end = Math.min(size, position + count);
long sizeToCopy = end - position;
if (sizeToCopy <= 0) {
return 0;
}
ByteBuffer buffer = ByteBuffer.allocate((int) sizeToCopy);
copyTo(buffer, (int) position, (int) (count));
buffer.rewind();
return target.write(buffer);
}
/**
* This method returns a string using the default charset.
*/
public String toString() {
return new String(toByteArray());
}
/**
* This method returns a string converted to the proper charset.
*/
public String toString(String charsetName) throws UnsupportedEncodingException {
return new String(toByteArray(), charsetName);
}
}