/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* 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 de.mxro.thrd.jdbm2V22.recman;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import de.mxro.thrd.jdbm2V22.helper.LongPacker;
/**
* This class wraps a page-sized byte array and provides methods
* to read and write data to and from it. The readers and writers
* are just the ones that the rest of the toolkit needs, nothing else.
* Values written are compatible with java.io routines.
*
* @see java.io.DataInput
* @see java.io.DataOutput
*/
public final class BlockIo {
private long blockId;
private byte[] data; // work area
transient private BlockView view = null;
private boolean dirty = false;
private int transactionCount = 0;
/**
* Default constructor for serialization
*/
public BlockIo() {
// empty
}
/**
* Constructs a new BlockIo instance working on the indicated
* buffer.
*/
BlockIo(long blockId, byte[] data) {
this.blockId = blockId;
this.data = data;
}
/**
* Returns the underlying array
*/
byte[] getData() {
return data;
}
/**
* Sets the block number. Should only be called by RecordFile.
*/
void setBlockId(long id) {
if (isInTransaction())
throw new Error("BlockId assigned for transaction block");
blockId = id;
}
/**
* Returns the block number.
*/
long getBlockId() {
return blockId;
}
/**
* Returns the current view of the block.
*/
public BlockView getView() {
return view;
}
/**
* Sets the current view of the block.
*/
public void setView(BlockView view) {
this.view = view;
}
/**
* Sets the dirty flag
*/
void setDirty() {
dirty = true;
}
/**
* Clears the dirty flag
*/
void setClean() {
dirty = false;
}
/**
* Returns true if the dirty flag is set.
*/
boolean isDirty() {
return dirty;
}
/**
* Returns true if the block is still dirty with respect to the
* transaction log.
*/
boolean isInTransaction() {
return transactionCount != 0;
}
/**
* Increments transaction count for this block, to signal that this
* block is in the log but not yet in the data file. The method also
* takes a snapshot so that the data may be modified in new transactions.
*/
synchronized void incrementTransactionCount() {
transactionCount++;
// @fixme(alex)
setClean();
}
/**
* Decrements transaction count for this block, to signal that this
* block has been written from the log to the data file.
*/
synchronized void decrementTransactionCount() {
transactionCount--;
if (transactionCount < 0)
throw new Error("transaction count on block "
+ getBlockId() + " below zero!");
}
/**
* Reads a byte from the indicated position
*/
public byte readByte(int pos) {
return data[pos];
}
/**
* Writes a byte to the indicated position
*/
public void writeByte(int pos, byte value) {
data[pos] = value;
setDirty();
}
/**
* Reads a short from the indicated position
*/
public short readShort(int pos) {
return (short)
(((short) (data[pos+0] & 0xff) << 8) |
((short) (data[pos+1] & 0xff) << 0));
}
/**
* Writes a short to the indicated position
*/
public void writeShort(int pos, short value) {
data[pos+0] = (byte)(0xff & (value >> 8));
data[pos+1] = (byte)(0xff & (value >> 0));
setDirty();
}
/**
* Reads an int from the indicated position
*/
public int readInt(int pos) {
return
(((int)(data[pos+0] & 0xff) << 24) |
((int)(data[pos+1] & 0xff) << 16) |
((int)(data[pos+2] & 0xff) << 8) |
((int)(data[pos+3] & 0xff) << 0));
}
/**
* Writes an int to the indicated position
*/
public void writeInt(int pos, int value) {
data[pos+0] = (byte)(0xff & (value >> 24));
data[pos+1] = (byte)(0xff & (value >> 16));
data[pos+2] = (byte)(0xff & (value >> 8));
data[pos+3] = (byte)(0xff & (value >> 0));
setDirty();
}
static final int ThreeByteInt_MAX = 256 * 256 * 256 -1;
/**
* Reads an int from the indicated position
*/
public int readThreeByteInt(int pos) {
return
(
((int)(data[pos+0] & 0xff) << 16) |
((int)(data[pos+1] & 0xff) << 8) |
((int)(data[pos+2] & 0xff) << 0));
}
/**
* Writes an int to the indicated position
*/
public void writeThreeByteInt(int pos, int value) {
if(value<0 || value>ThreeByteInt_MAX)
throw new IllegalArgumentException("out of range: "+value);
data[pos+0] = (byte)(0xff & (value >> 16));
data[pos+1] = (byte)(0xff & (value >> 8));
data[pos+2] = (byte)(0xff & (value >> 0));
setDirty();
}
/**
* Reads a long from the indicated position
*/
public long readLong( int pos )
{
// Contributed by Erwin Bolwidt <ejb@klomp.org>
// Gives about 15% performance improvement
return
( (long)( ((data[pos+0] & 0xff) << 24) |
((data[pos+1] & 0xff) << 16) |
((data[pos+2] & 0xff) << 8) |
((data[pos+3] & 0xff) ) ) << 32 ) |
( (long)( ((data[pos+4] & 0xff) << 24) |
((data[pos+5] & 0xff) << 16) |
((data[pos+6] & 0xff) << 8) |
((data[pos+7] & 0xff) ) ) & 0xffffffff );
/* Original version by Alex Boisvert. Might be faster on 64-bit JVMs.
return
(((long)(data[pos+0] & 0xff) << 56) |
((long)(data[pos+1] & 0xff) << 48) |
((long)(data[pos+2] & 0xff) << 40) |
((long)(data[pos+3] & 0xff) << 32) |
((long)(data[pos+4] & 0xff) << 24) |
((long)(data[pos+5] & 0xff) << 16) |
((long)(data[pos+6] & 0xff) << 8) |
((long)(data[pos+7] & 0xff) << 0));
*/
}
/**
* Writes a long to the indicated position
*/
public void writeLong(int pos, long value) {
data[pos+0] = (byte)(0xff & (value >> 56));
data[pos+1] = (byte)(0xff & (value >> 48));
data[pos+2] = (byte)(0xff & (value >> 40));
data[pos+3] = (byte)(0xff & (value >> 32));
data[pos+4] = (byte)(0xff & (value >> 24));
data[pos+5] = (byte)(0xff & (value >> 16));
data[pos+6] = (byte)(0xff & (value >> 8));
data[pos+7] = (byte)(0xff & (value >> 0));
setDirty();
}
/**
* Reads a long from the indicated position
*/
public long readSixByteLong( int pos )
{
return
(((long)(data[pos+0] & 0xff) << 40) |
((long)(data[pos+1] & 0xff) << 32) |
((long)(data[pos+2] & 0xff) << 24) |
((long)(data[pos+3] & 0xff) << 16) |
((long)(data[pos+4] & 0xff) << 8) |
((long)(data[pos+5] & 0xff) << 0));
}
/**
* Writes a long to the indicated position
*/
public void writeSixByteLong(int pos, long value) {
// if(value >> (6*8)!=0)
// throw new IllegalArgumentException("does not fit");
data[pos+0] = (byte)(0xff & (value >> 40));
data[pos+1] = (byte)(0xff & (value >> 32));
data[pos+2] = (byte)(0xff & (value >> 24));
data[pos+3] = (byte)(0xff & (value >> 16));
data[pos+4] = (byte)(0xff & (value >> 8 ));
data[pos+5] = (byte)(0xff & (value >> 0 ));
setDirty();
}
// overrides java.lang.Object
public String toString() {
return "BlockIO("
+ blockId + ","
+ dirty + ","
+ view + ")";
}
// implement externalizable interface
public void readExternal(DataInputStream in)
throws IOException, ClassNotFoundException {
blockId = LongPacker.unpackLong(in);
int length = LongPacker.unpackInt(in);
data = new byte[length];
in.readFully(data);
}
// implement externalizable interface
public void writeExternal(DataOutputStream out) throws IOException {
LongPacker.packLong(out, blockId);
LongPacker.packInt(out, data.length);
out.write(data);
}
static final int UNSIGNED_SHORT_MAX = 256 * 256 -1;
public void writeUnsignedShort(int pos, int value) {
if(value>UNSIGNED_SHORT_MAX || value<0)
throw new IllegalArgumentException("Out of range: "+value);
data[pos+0] = (byte)(0xff & (value >> 8));
data[pos+1] = (byte)(0xff & (value >> 0));
setDirty();
}
public int readUnsignedshort(int pos){
return
(((int)(data[pos+0] & 0xff) << 8) |
((int)(data[pos+1] & 0xff) << 0));
}
}