/*
* Copyright (C) 2015 SoftIndex LLC.
*
* 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 io.datakernel.serializer;
import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.bytebuf.ByteBufPool;
import io.datakernel.bytebuf.SerializationUtils;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
public final class DataOutputStreamEx implements Closeable {
private static final SerializeException SIZE_EXCEPTION = new SerializeException("Message size of out range");
public static final int DEFAULT_BUFFER_SIZE = 65536;
public static final int MAX_SIZE_127 = 1; // (1 << (1 * 7)) - 1
public static final int MAX_SIZE_16K = 2; // (1 << (2 * 7)) - 1
public static final int MAX_SIZE_2M = 3; // (1 << (3 * 7)) - 1
private OutputStream outputStream;
private ByteBuf buf;
private int estimatedMessageSize = 1;
private DataOutputStreamEx(OutputStream outputStream, int bufferSize) {
this.outputStream = outputStream;
this.buf = ByteBufPool.allocate(bufferSize);
}
public static DataOutputStreamEx create(OutputStream output) {
return new DataOutputStreamEx(output, DEFAULT_BUFFER_SIZE);
}
public static DataOutputStreamEx create(OutputStream outputStream, int bufferSize) {
return new DataOutputStreamEx(outputStream, bufferSize);
}
public void changeOutputStream(OutputStream outputStream) throws IOException {
if (this.outputStream != null) {
flush();
this.outputStream.close();
}
this.outputStream = outputStream;
}
private void doEnsureSize(int size) throws IOException {
// flush previous values before resize
doFlush();
if (buf.writeRemaining() < size) {
buf.recycle();
buf = ByteBufPool.allocate(size);
}
}
private void ensureSize(int size) throws IOException {
if (buf.writeRemaining() < size) {
doEnsureSize(size);
}
}
private void doFlush() throws IOException {
if (buf.canRead()) {
outputStream.write(buf.array(), buf.readPosition(), buf.readRemaining());
buf.readPosition(0);
buf.writePosition(0);
}
}
public void flush() throws IOException {
doFlush();
outputStream.flush();
}
@Override
public void close() throws IOException {
flush();
outputStream.close();
buf.recycle();
}
private static void writeSize(byte[] buf, int pos, int size, int headerSize) {
if (headerSize == 1) {
buf[pos] = (byte) size;
} else {
buf[pos] = (byte) (size | 0x80);
size >>>= 7;
if (headerSize == 2) {
buf[pos + 1] = (byte) size;
} else {
buf[pos + 1] = (byte) (size | 0x80);
size >>>= 7;
assert headerSize == 3;
buf[pos + 2] = (byte) size;
}
}
}
public <T> void serialize(BufferSerializer<T> serializer, T value, int headerSize) throws IOException, SerializeException {
int positionBegin;
int positionItem;
for (; ; ) {
ensureSize(headerSize + estimatedMessageSize);
positionBegin = buf.writePosition();
positionItem = positionBegin + headerSize;
buf.writePosition(positionItem);
try {
serializer.serialize(buf, value);
} catch (ArrayIndexOutOfBoundsException e) {
int messageSize = buf.limit() - positionItem;
buf.writePosition(positionBegin);
estimatedMessageSize = messageSize + 1 + (messageSize >>> 1);
continue;
} catch (Exception e) {
buf.writePosition(positionBegin);
throw new SerializeException(e);
}
break;
}
int positionEnd = buf.writePosition();
int messageSize = positionEnd - positionItem;
if (messageSize >= 1 << headerSize * 7) {
buf.writePosition(positionBegin);
throw SIZE_EXCEPTION;
}
writeSize(buf.array(), positionBegin, messageSize, headerSize);
messageSize += messageSize >>> 2;
if (messageSize > estimatedMessageSize)
estimatedMessageSize = messageSize;
else
estimatedMessageSize -= estimatedMessageSize >>> 10;
}
public void write(byte[] b) throws IOException {
ensureSize(b.length);
int newTail = SerializationUtils.write(buf.array(), buf.writePosition(), b);
buf.writePosition(newTail);
}
public void write(byte[] b, int off, int len) throws IOException {
ensureSize(len);
int newTail = SerializationUtils.write(buf.array(), buf.writePosition(), b, off, len);
buf.writePosition(newTail);
}
public void writeBoolean(boolean v) throws IOException {
ensureSize(1);
int newTail = SerializationUtils.writeBoolean(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeByte(byte v) throws IOException {
ensureSize(1);
int newTail = SerializationUtils.writeByte(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeShort(short v) throws IOException {
ensureSize(2);
int newTail = SerializationUtils.writeShort(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeInt(int v) throws IOException {
ensureSize(4);
int newTail = SerializationUtils.writeInt(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeLong(long v) throws IOException {
ensureSize(8);
int newTail = SerializationUtils.writeLong(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeVarInt(int v) throws IOException {
ensureSize(5);
int newTail = SerializationUtils.writeVarInt(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeVarLong(long v) throws IOException {
ensureSize(9);
int newTail = SerializationUtils.writeVarLong(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeFloat(float v) throws IOException {
ensureSize(4);
int newTail = SerializationUtils.writeFloat(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeDouble(double v) throws IOException {
ensureSize(8);
int newTail = SerializationUtils.writeDouble(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeChar(char v) throws IOException {
ensureSize(2);
int newTail = SerializationUtils.writeChar(buf.array(), buf.writePosition(), v);
buf.writePosition(newTail);
}
public void writeUTF8(String s) throws IOException {
ensureSize(5 + s.length() * 3);
int newTail = SerializationUtils.writeJavaUTF8(buf.array(), buf.writePosition(), s);
buf.writePosition(newTail);
}
public void writeIso88591(String s) throws IOException {
ensureSize(5 + s.length() * 3);
int newTail = SerializationUtils.writeIso88591(buf.array(), buf.writePosition(), s);
buf.writePosition(newTail);
}
public final void writeUTF16(String s) throws IOException {
ensureSize(5 + s.length() * 2);
int newTail = SerializationUtils.writeUTF16(buf.array(), buf.writePosition(), s);
buf.writePosition(newTail);
}
}