/*
* Copyright 2009-2016 Tilmann Zaeschke. All rights reserved.
*
* This file is part of ZooDB.
*
* ZooDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZooDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ZooDB. If not, see <http://www.gnu.org/licenses/>.
*
* See the README and COPYING files for further information.
*/
package org.zoodb.internal;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.zoodb.internal.server.DiskIO;
import org.zoodb.internal.server.ObjectWriter;
/**
*
* @author Tilmann Zaschke
*
*/
class GenericObjectWriter implements ObjectWriter, DiskIO {
private ByteBuffer buf;
private final long headerClassOid;
private final long headerTxTimestamp;
private int MAX_POS;
public GenericObjectWriter(int nBytes, long clsOid, long txTimestamp) {
this.buf = ByteBuffer.allocate(nBytes);
this.headerClassOid = clsOid;
this.headerTxTimestamp = txTimestamp;
this.MAX_POS = nBytes;
}
@Override
public void startObject(long oid, int prevSchemaVersion) {
//nothing to do...
}
@Override
public void finishObject() {
//nothing to do...
}
/**
* This can be necessary when subsequent objects are of a different class.
*/
@Override
public void newPage() {
writeHeader();
}
private void writeHeader() {
writeLong(headerClassOid);
writeLong(headerTxTimestamp);
}
@Override
public void writeString(String string) {
checkPosWrite(4);
buf.putInt(string.length());
//Align for 2-byte writing
int p = buf.position();
if ((p & 0x00000001) == 1) {
buf.position(p+1);
}
CharBuffer cb;
int l = string.length();
int posA = 0; //position in array
while (l > 0) {
checkPosWrite(2);
int putLen = MAX_POS - buf.position();
putLen = putLen >> 1; //TODO loses odd values!
if (putLen > l) {
putLen = l;
}
//This is crazy!!! Unlike ByteBuffer, CharBuffer requires END as third param!!
cb = buf.asCharBuffer();
cb.put(string, posA, posA + putLen);
buf.position(buf.position() + putLen * 2);
posA += putLen;
l -= putLen;
}
}
@Override
public void write(byte[] array) {
int l = array.length;
int posA = 0; //position in array
while (l > 0) {
checkPosWrite(1);
int putLen = MAX_POS - buf.position();
if (putLen > l) {
putLen = l;
}
buf.put(array, posA, putLen);
posA += putLen;
l -= putLen;
}
}
@Override
public void writeBoolean(boolean boolean1) {
writeByte((byte) (boolean1 ? 1 : 0));
}
@Override
public void writeByte(byte byte1) {
checkPosWrite(S_BYTE);
buf.put(byte1);
}
@Override
public void writeChar(char char1) {
if (!checkPos(S_CHAR)) {
write(ByteBuffer.allocate(S_CHAR).putChar(char1).array());
return;
}
buf.putChar(char1);
}
@Override
public void writeDouble(double double1) {
if (!checkPos(S_DOUBLE)) {
writeLong(Double.doubleToLongBits(double1));
return;
}
buf.putDouble(double1);
}
@Override
public void writeFloat(float float1) {
if (!checkPos(S_FLOAT)) {
writeInt(Float.floatToIntBits(float1));
return;
}
buf.putFloat(float1);
}
@Override
public void writeInt(int int1) {
if (!checkPos(S_INT)) {
write(ByteBuffer.allocate(S_INT).putInt(int1).array());
return;
}
buf.putInt(int1);
}
@Override
public void writeLong(long long1) {
if (!checkPos(S_LONG)) {
write(ByteBuffer.allocate(S_LONG).putLong(long1).array());
return;
}
buf.putLong(long1);
}
@Override
public void writeShort(short short1) {
if (!checkPos(S_SHORT)) {
write(ByteBuffer.allocate(S_SHORT).putShort(short1).array());
return;
}
buf.putShort(short1);
}
private boolean checkPos(int delta) {
//TODO remove autopaging, the indices use anyway the noCheckMethods!!
//TODO -> otherwise, make it final, as it should be known when a view is constructed.
// if (isAutoPaging) {
// return (buf.position() + delta - MAX_POS) <= 0;
// }
return true;
}
private void checkPosWrite(int delta) {
if (buf.position() + delta > MAX_POS) {
MAX_POS = buf.capacity()*2 + delta;
System.err.println("FIXME: Resizing buffer (internally)! " + MAX_POS);
ByteBuffer b2 = ByteBuffer.allocate(MAX_POS);
buf.flip();
b2.put(buf);
buf = b2;
//throw new ArrayIndexOutOfBoundsException((buf.position() + delta) + " > " + MAX_POS);
}
}
@Override
public void skipWrite(int nBytes) {
int l = nBytes;
while (l > 0) {
checkPosWrite(1);
int bPos = buf.position();
int putLen = MAX_POS - bPos;
if (putLen > l) {
putLen = l;
}
buf.position(bPos + putLen);
l -= putLen;
}
}
@Override
public void flush() {
//nothing to do...
}
public ByteBuffer toByteArray() {
return buf;
}
}