/*
* Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved.
*
* 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 com.hazelcast.nio;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
public final class FastByteArrayOutputStream extends ByteArrayOutputStream implements DataOutput {
private final byte writeBuffer[] = new byte[8];
static final int STRING_CHUNK_SIZE = 16 * 1024;
public FastByteArrayOutputStream() {
this(32);
}
public FastByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
@Override
public void write(int b) {
int newcount = count + 1;
if (newcount > buf.length) {
byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
buf[count] = (byte) b;
count = newcount;
}
@Override
public void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
int newcount = count + len;
if (newcount > buf.length) {
byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
System.arraycopy(b, off, buf, count, len);
count = newcount;
}
@Override
public void writeTo(OutputStream out) throws IOException {
out.write(buf, 0, count);
}
public void set(byte[] buffer) {
this.buf = buffer;
}
@Override
public void reset() {
count = 0;
}
public int getCount() {
return count;
}
@Override
public byte toByteArray()[] {
byte newbuf[] = new byte[count];
System.arraycopy(buf, 0, newbuf, 0, count);
return newbuf;
}
public byte[] getBytes() {
return buf;
}
public final void writeBoolean(final boolean v) throws IOException {
write(v ? 1 : 0);
}
public final void writeByte(final int v) throws IOException {
write(v);
}
public final void writeBytes(final String s) throws IOException {
final int len = s.length();
for (int i = 0; i < len; i++) {
write((byte) s.charAt(i));
}
}
public final void writeChar(final int v) throws IOException {
write((v >>> 8) & 0xFF);
write((v) & 0xFF);
}
public final void writeChars(final String s) throws IOException {
final int len = s.length();
for (int i = 0; i < len; i++) {
final int v = s.charAt(i);
write((v >>> 8) & 0xFF);
write((v) & 0xFF);
}
}
public final void writeDouble(final double v) throws IOException {
writeLong(Double.doubleToLongBits(v));
}
public final void writeFloat(final float v) throws IOException {
writeInt(Float.floatToIntBits(v));
}
public final void writeInt(final int v) throws IOException {
write((v >>> 24) & 0xFF);
write((v >>> 16) & 0xFF);
write((v >>> 8) & 0xFF);
write((v) & 0xFF);
}
public final void writeLong(final long v) throws IOException {
writeBuffer[0] = (byte) (v >>> 56);
writeBuffer[1] = (byte) (v >>> 48);
writeBuffer[2] = (byte) (v >>> 40);
writeBuffer[3] = (byte) (v >>> 32);
writeBuffer[4] = (byte) (v >>> 24);
writeBuffer[5] = (byte) (v >>> 16);
writeBuffer[6] = (byte) (v >>> 8);
writeBuffer[7] = (byte) (v);
write(writeBuffer, 0, 8);
}
public final void writeShort(final int v) throws IOException {
write((v >>> 8) & 0xFF);
write((v) & 0xFF);
}
public final void writeUTF(final String str) throws IOException {
boolean isNull = (str == null);
writeBoolean(isNull);
if (isNull) return;
int length = str.length();
writeInt(length);
int chunkSize = length / STRING_CHUNK_SIZE + 1;
for (int i = 0; i < chunkSize; i++) {
int beginIndex = Math.max(0, i * STRING_CHUNK_SIZE - 1);
int endIndex = Math.min((i + 1) * STRING_CHUNK_SIZE - 1, length);
writeShortUTF(str.substring(beginIndex, endIndex));
}
}
private final void writeShortUTF(final String str) throws IOException {
final int strlen = str.length();
int utflen = 0;
int c, count = 0;
/* use charAt instead of copying String to char array */
for (int i = 0; i < strlen; i++) {
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
utflen++;
} else if (c > 0x07FF) {
utflen += 3;
} else {
utflen += 2;
}
}
// if (utflen > 65535)
// throw new UTFDataFormatException("encoded string too long: " + utflen + " bytes");
final byte[] bytearr = new byte[utflen + 2];
bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
bytearr[count++] = (byte) ((utflen) & 0xFF);
int i;
for (i = 0; i < strlen; i++) {
c = str.charAt(i);
if (!((c >= 0x0001) && (c <= 0x007F)))
break;
bytearr[count++] = (byte) c;
}
for (; i < strlen; i++) {
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
bytearr[count++] = (byte) c;
} else if (c > 0x07FF) {
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
} else {
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
}
}
write(bytearr, 0, utflen + 2);
}
}