/*******************************************************************************
* Copyright (c) 2005, 2016 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX - Initial API and implementation
* Markus Schorn (Wind River Systems)
* IBM Corporation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.jdt.internal.core.nd.db;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Caches the content of a piece of the database.
*/
final class Chunk {
final private byte[] fBuffer= new byte[Database.CHUNK_SIZE];
final Database fDatabase;
final int fSequenceNumber;
boolean fCacheHitFlag;
boolean fDirty;
boolean fLocked; // locked chunks must not be released from cache.
int fCacheIndex= -1;
Chunk(Database db, int sequenceNumber) {
this.fDatabase= db;
this.fSequenceNumber= sequenceNumber;
}
void read() throws IndexException {
try {
final ByteBuffer buf= ByteBuffer.wrap(this.fBuffer);
this.fDatabase.read(buf, (long) this.fSequenceNumber * Database.CHUNK_SIZE);
} catch (IOException e) {
throw new IndexException(new DBStatus(e));
}
}
/**
* Uninterruptable. Returns true iff an attempt was made to interrupt the flush with
* {@link Thread#interrupt()}.
*/
boolean flush() throws IndexException {
boolean wasCanceled = false;
try {
final ByteBuffer buf= ByteBuffer.wrap(this.fBuffer);
wasCanceled = this.fDatabase.write(buf, (long) this.fSequenceNumber * Database.CHUNK_SIZE);
} catch (IOException e) {
throw new IndexException(new DBStatus(e));
}
this.fDirty= false;
return wasCanceled;
}
private static int recPtrToIndex(final long offset) {
return (int) (offset & Database.OFFSET_IN_CHUNK_MASK);
}
public void putByte(final long offset, final byte value) {
assert this.fLocked;
this.fDirty= true;
this.fBuffer[recPtrToIndex(offset)]= value;
}
public byte getByte(final long offset) {
return this.fBuffer[recPtrToIndex(offset)];
}
public byte[] getBytes(final long offset, final int length) {
final byte[] bytes = new byte[length];
System.arraycopy(this.fBuffer, recPtrToIndex(offset), bytes, 0, length);
return bytes;
}
public void putBytes(final long offset, final byte[] bytes) {
assert this.fLocked;
this.fDirty= true;
System.arraycopy(bytes, 0, this.fBuffer, recPtrToIndex(offset), bytes.length);
}
public void putInt(final long offset, final int value) {
assert this.fLocked;
this.fDirty= true;
int idx= recPtrToIndex(offset);
putInt(value, this.fBuffer, idx);
}
static final void putInt(final int value, final byte[] buffer, int idx) {
buffer[idx]= (byte) (value >> 24);
buffer[++idx]= (byte) (value >> 16);
buffer[++idx]= (byte) (value >> 8);
buffer[++idx]= (byte) (value);
}
public int getInt(final long offset) {
return getInt(this.fBuffer, recPtrToIndex(offset));
}
static final int getInt(final byte[] buffer, int idx) {
return ((buffer[idx] & 0xff) << 24) |
((buffer[++idx] & 0xff) << 16) |
((buffer[++idx] & 0xff) << 8) |
((buffer[++idx] & 0xff) << 0);
}
/**
* A free Record Pointer is a pointer to a raw block, i.e. the
* pointer is not moved past the BLOCK_HEADER_SIZE.
*/
static int compressFreeRecPtr(final long value) {
// This assert verifies the alignment. We expect the low bits to be clear.
assert (value & (Database.BLOCK_SIZE_DELTA - 1)) == 0;
final int dense = (int) (value >> Database.BLOCK_SIZE_DELTA_BITS);
return dense;
}
/**
* A free Record Pointer is a pointer to a raw block,
* i.e. the pointer is not moved past the BLOCK_HEADER_SIZE.
*/
static long expandToFreeRecPtr(int value) {
/*
* We need to properly manage the integer that was read. The value will be sign-extended
* so if the most significant bit is set, the resulting long will look negative. By
* masking it with ((long)1 << 32) - 1 we remove all the sign-extended bits and just
* have an unsigned 32-bit value as a long. This gives us one more useful bit in the
* stored record pointers.
*/
long address = value & 0xFFFFFFFFL;
return address << Database.BLOCK_SIZE_DELTA_BITS;
}
/**
* A Record Pointer is a pointer as returned by Database.malloc().
* This is a pointer to a block + BLOCK_HEADER_SIZE.
*/
public void putRecPtr(final long offset, final long value) {
assert this.fLocked;
this.fDirty = true;
int idx = recPtrToIndex(offset);
Database.putRecPtr(value, this.fBuffer, idx);
}
/**
* A free Record Pointer is a pointer to a raw block,
* i.e. the pointer is not moved past the BLOCK_HEADER_SIZE.
*/
public void putFreeRecPtr(final long offset, final long value) {
assert this.fLocked;
this.fDirty = true;
int idx = recPtrToIndex(offset);
putInt(compressFreeRecPtr(value), this.fBuffer, idx);
}
public long getRecPtr(final long offset) {
final int idx = recPtrToIndex(offset);
return Database.getRecPtr(this.fBuffer, idx);
}
public long getFreeRecPtr(final long offset) {
final int idx = recPtrToIndex(offset);
int value = getInt(this.fBuffer, idx);
return expandToFreeRecPtr(value);
}
public void put3ByteUnsignedInt(final long offset, final int value) {
assert this.fLocked;
this.fDirty= true;
int idx= recPtrToIndex(offset);
this.fBuffer[idx]= (byte) (value >> 16);
this.fBuffer[++idx]= (byte) (value >> 8);
this.fBuffer[++idx]= (byte) (value);
}
public int get3ByteUnsignedInt(final long offset) {
int idx= recPtrToIndex(offset);
return ((this.fBuffer[idx] & 0xff) << 16) |
((this.fBuffer[++idx] & 0xff) << 8) |
((this.fBuffer[++idx] & 0xff) << 0);
}
public void putShort(final long offset, final short value) {
assert this.fLocked;
this.fDirty= true;
int idx= recPtrToIndex(offset);
this.fBuffer[idx]= (byte) (value >> 8);
this.fBuffer[++idx]= (byte) (value);
}
public short getShort(final long offset) {
int idx= recPtrToIndex(offset);
return (short) (((this.fBuffer[idx] << 8) | (this.fBuffer[++idx] & 0xff)));
}
public long getLong(final long offset) {
int idx= recPtrToIndex(offset);
return ((((long) this.fBuffer[idx] & 0xff) << 56) |
(((long) this.fBuffer[++idx] & 0xff) << 48) |
(((long) this.fBuffer[++idx] & 0xff) << 40) |
(((long) this.fBuffer[++idx] & 0xff) << 32) |
(((long) this.fBuffer[++idx] & 0xff) << 24) |
(((long) this.fBuffer[++idx] & 0xff) << 16) |
(((long) this.fBuffer[++idx] & 0xff) << 8) |
(((long) this.fBuffer[++idx] & 0xff) << 0));
}
public double getDouble(long offset) {
return Double.longBitsToDouble(getLong(offset));
}
public float getFloat(long offset) {
return Float.intBitsToFloat(getInt(offset));
}
public void putLong(final long offset, final long value) {
assert this.fLocked;
this.fDirty= true;
int idx= recPtrToIndex(offset);
this.fBuffer[idx]= (byte) (value >> 56);
this.fBuffer[++idx]= (byte) (value >> 48);
this.fBuffer[++idx]= (byte) (value >> 40);
this.fBuffer[++idx]= (byte) (value >> 32);
this.fBuffer[++idx]= (byte) (value >> 24);
this.fBuffer[++idx]= (byte) (value >> 16);
this.fBuffer[++idx]= (byte) (value >> 8);
this.fBuffer[++idx]= (byte) (value);
}
public void putChar(final long offset, final char value) {
assert this.fLocked;
this.fDirty= true;
int idx= recPtrToIndex(offset);
this.fBuffer[idx]= (byte) (value >> 8);
this.fBuffer[++idx]= (byte) (value);
}
public void putChars(final long offset, char[] chars, int start, int len) {
assert this.fLocked;
this.fDirty= true;
int idx= recPtrToIndex(offset)-1;
final int end= start + len;
for (int i = start; i < end; i++) {
char value= chars[i];
this.fBuffer[++idx]= (byte) (value >> 8);
this.fBuffer[++idx]= (byte) (value);
}
}
public void putCharsAsBytes(final long offset, char[] chars, int start, int len) {
assert this.fLocked;
this.fDirty= true;
int idx= recPtrToIndex(offset)-1;
final int end= start + len;
for (int i = start; i < end; i++) {
char value= chars[i];
this.fBuffer[++idx]= (byte) (value);
}
}
public void putDouble(final long offset, double value) {
putLong(offset, Double.doubleToLongBits(value));
}
public void putFloat(final long offset, float value) {
putInt(offset, Float.floatToIntBits(value));
}
public char getChar(final long offset) {
int idx= recPtrToIndex(offset);
return (char) (((this.fBuffer[idx] << 8) | (this.fBuffer[++idx] & 0xff)));
}
public void getChars(final long offset, final char[] result, int start, int len) {
final ByteBuffer buf= ByteBuffer.wrap(this.fBuffer);
buf.position(recPtrToIndex(offset));
buf.asCharBuffer().get(result, start, len);
}
public void getCharsFromBytes(final long offset, final char[] result, int start, int len) {
final int pos = recPtrToIndex(offset);
for (int i = 0; i < len; i++) {
result[start + i] = (char) (this.fBuffer[pos + i] & 0xff);
}
}
void clear(final long offset, final int length) {
assert this.fLocked;
this.fDirty= true;
int idx = recPtrToIndex(offset);
final int end = idx + length;
for (; idx < end; idx++) {
this.fBuffer[idx] = 0;
}
}
void put(final long offset, final byte[] data, final int len) {
put(offset, data, 0, len);
}
void put(final long offset, final byte[] data, int dataPos, final int len) {
assert this.fLocked;
this.fDirty = true;
int idx = recPtrToIndex(offset);
System.arraycopy(data, dataPos, this.fBuffer, idx, len);
}
public void get(final long offset, byte[] data) {
get(offset, data, 0, data.length);
}
public void get(final long offset, byte[] data, int dataPos, int len) {
int idx = recPtrToIndex(offset);
System.arraycopy(this.fBuffer, idx, data, dataPos, len);
}
}