All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zoodb.jdo.internal.server.StorageReader Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2009-2013 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 .
 * 
 * See the README and COPYING files for further information. 
 */
package org.zoodb.jdo.internal.server;


import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;

import org.zoodb.jdo.internal.server.index.BitTools;
import org.zoodb.jdo.internal.util.DBLogger;

public class StorageReader implements StorageChannelInput {

	private final ByteBuffer buf;
	private int currentPage = -1;
	
	//indicate whether to automatically allocate and move to next page when page end is reached.
	private final boolean isAutoPaging;
	//The header is only written in auto-paging mode
	private long pageHeader = -1;
	
	private final int MAX_POS;
	
	private final StorageChannel root;
	private final IntBuffer intBuffer;
	private final int[] intArray;
	
	private CallbackPageRead overflowCallback = null;
	private DATA_TYPE currentType;

	/**
	 * Use for creating an additional view on a given file.
	 * @param fc
	 * @param pageSize
	 * @param fsm
	 */
	StorageReader(StorageChannel root, boolean autoPaging) {
		this.root = root; 
		this.MAX_POS = root.getPageSize() - 4;
		this.isAutoPaging = autoPaging;
		
		buf = ByteBuffer.allocateDirect(root.getPageSize());
		currentPage = -1;
		intBuffer = buf.asIntBuffer();
		intArray = new int[intBuffer.capacity()];
	}

	/**
	 * To be called after every commit, to ensure that pages are reset, in case they have been 
	 * rewritten.
	 */
	@Override
	public void reset() {
		currentPage = -1;
	}
	
	@Override
	public void seekPageForRead(DATA_TYPE type, int pageId) {
	    seekPage(type, pageId, 0);
	}
	
	
	@Override
	public void seekPosAP(DATA_TYPE type, long pageAndOffs) {
		int page = BitTools.getPage(pageAndOffs);
		int offs = BitTools.getOffs(pageAndOffs);
		seekPage(type, page, offs);
	}

	@Override
	public void seekPage(DATA_TYPE type, int pageId, int pageOffset) {
		//isAutoPaging = autoPaging;

		if (pageId != currentPage) {
			currentPage = pageId;
			buf.clear();
			root.readPage(buf, pageId);
		}

		currentType = type;
		if (type != DATA_TYPE.DB_HEADER) {
			buf.clear();
			readHeader();
		}
		
		if (pageOffset == 0) {
			if (isAutoPaging) {
				pageOffset = PAGE_HEADER_SIZE_DATA; //TODO this is dirty...
			} else {
				if (type != DATA_TYPE.DB_HEADER) {
					pageOffset = PAGE_HEADER_SIZE;
				}
			}
		}
		buf.position(pageOffset);
	}

	@Override
	public String readString() {
		checkPosRead(4);
		int len = buf.getInt();

		//Align for 2-byte writing
		int p = buf.position();
		if ((p & 0x00000001) == 1) {
			buf.position(p+1);
		}

		char[] array = new char[len];
		CharBuffer cb = buf.asCharBuffer();
		int l = array.length;
        int posA = 0; //position in array
        while (l > 0) {
            checkPosRead(2);
            int getLen = MAX_POS - buf.position();
            getLen = getLen >> 1;
            if (getLen > l) {
                getLen = l;
            }
            cb = buf.asCharBuffer(); //create a buffer at the correct position
            cb.get(array, posA, getLen);
            buf.position(buf.position()+getLen*2);
            posA += getLen;
            l -= getLen;
        }
        return String.valueOf(array);
        //return String.copyValueOf(array);
        //return new String(array);
	}

	
	@Override
	public boolean readBoolean() {
        return readByte() != 0;
	}

	@Override
	public byte readByte() {
		checkPosRead(S_BYTE);
		return buf.get();
	}

	@Override
	public char readChar() {
		if (!checkPos(S_CHAR)) {
			return readByteBuffer(S_CHAR).getChar();
		}
		return buf.getChar();
	}

	@Override
	public double readDouble() {
		if (!checkPos(S_DOUBLE)) {
			return Double.longBitsToDouble(readLong());
		}
		return buf.getDouble();
	}

	@Override
	public float readFloat() {
		if (!checkPos(S_FLOAT)) {
			return Float.intBitsToFloat(readInt());
		}
		return buf.getFloat();
	}

	@Override
	public void readFully(byte[] array) {
        int l = array.length;
        int posA = 0; //position in array
        while (l > 0) {
            checkPosRead(1);
            int getLen = MAX_POS - buf.position();
            if (getLen > l) {
                getLen = l;
            }
            buf.get(array, posA, getLen);
            posA += getLen;
            l -= getLen;
        }
	}

	@Override
	public void noCheckRead(long[] array) {
		LongBuffer lb = buf.asLongBuffer();
		lb.get(array);
	    buf.position(buf.position() + S_LONG * array.length);
	}
	
	@Override
	public void noCheckRead(int[] array) {
		IntBuffer lb = buf.asIntBuffer();
		lb.get(array);
	    buf.position(buf.position() + S_INT * array.length);
	}
	
	@Override
	public void noCheckRead(byte[] array) {
		buf.get(array);
	}
	
	@Override
	public void noCheckReadAsInt(long[] array, int nElements) {
		int pos = buf.position();
		if ((pos >> 2) << 2 == pos) {
			intBuffer.position(pos >> 2);
		} else {
			intBuffer.position((pos >> 2)+1);
		}
		intBuffer.get(intArray, 0, nElements);
		for (int i = 0; i < nElements; i++) {
			array[i] = intArray[i];
		}
	    buf.position(intBuffer.position() * S_INT);

	  //Alternative implementation (faster according to PerfTest but slower when running JUnit suite
//		for (int i = 0; i < nElements; i++) {
//			array[i] = buf.getInt();
//		}
	}
	
	@Override
	public int readInt() {
		if (!checkPos(S_INT)) {
			return readByteBuffer(S_INT).getInt();
		}
        return buf.getInt();
	}

	@Override
	public long readLong() {
		if (!checkPos(S_LONG)) {
			return readByteBuffer(S_LONG).getLong();
		}
//		checkPosRead(S_LONG);
		return buf.getLong();
	}

	@Override
	public short readShort() {
		if (!checkPos(S_SHORT)) {
			return readByteBuffer(S_SHORT).getShort();
		}
		return buf.getShort();
	}

	private ByteBuffer readByteBuffer(int len) {
		byte[] ba = new byte[len];
		readFully(ba);
		return ByteBuffer.wrap(ba);
	}
	
    @Override
    public long getHeaderClassOID() {
    	return pageHeader;
    }
	
	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 checkPosRead(int delta) {
		final ByteBuffer buf = this.buf;
		if (isAutoPaging && buf.position() + delta > MAX_POS) {
			final int pageId = buf.getInt();
			currentPage = pageId;
			buf.clear();
			root.readPage(buf, pageId);
			buf.rewind();
			//read header
			readHeader();
			if (overflowCallback != null) {
				overflowCallback.notifyOverflowRead(currentPage);
			}
		}
 	}

	private void readHeader() {
		byte pageType = buf.get();
//		byte dummy = buf.get();
//		short pageVersion = buf.getShort();
//		long txId = buf.getLong();
		if (pageType != currentType.getId()) {
			buf.get(); //dummy
			buf.getShort(); //pageVersion
			long txId = buf.getLong();
			throw DBLogger.newFatal("Page type mismatch, expected " + 
					currentType.getId() + "/" + currentType + " (tx=" + txId +
					") but got " + pageType + " (tx=" + root.getTxId() + 
					"). PageId=" + currentPage);
		}
		buf.position(PAGE_HEADER_SIZE);
		if (isAutoPaging) {
			pageHeader = buf.getLong();
		}
	}
	
    @Override
    public int getOffset() {
        return buf.position();
    }

    @Override
    public int getPage() {
        return currentPage;
    }
	
	@Override
	public void skipRead(int nBytes) {
        int l = nBytes;
        while (l > 0) {
            checkPosRead(1);
            int bPos = buf.position();
            int putLen = MAX_POS - bPos;
            if (putLen > l) {
                putLen = l;
            }
            buf.position(bPos + putLen);
            l -= putLen;
        }
	}

	@Override
	public void setOverflowCallbackRead(CallbackPageRead readCallback) {
		if (overflowCallback != null) {
			throw new IllegalStateException();
		}
		overflowCallback = readCallback;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy