package com.ctriposs.bigmap;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import com.ctriposs.bigmap.page.IMappedPage;
import com.ctriposs.bigmap.page.IMappedPageFactory;;
/**
* Helper class representing an entry pointing to the memory mapped map entry.
*
* @author bulldog
*
*/
public class MapEntry {
final static int INDEX_ITEM_DATA_PAGE_INDEX_OFFSET = 0;
final static int INDEX_ITEM_DATA_SLOT_OFFSET_OFFSET = 8;
final static int INDEX_ITEM_DATA_SLOT_LENGTH_OFFSET = 12;
final static int INDEX_ITEM_MAP_ENTRY_KEY_LENGTH_OFFSET = 16;
final static int INDEX_ITEM_MAP_ENTRY_VALUE_LENGTH_OFFSET = 20;
final static int INDEX_ITEM_MAP_ENTRY_CREATED_TIME_OFFSET = 24;
final static int INDEX_ITEM_MAP_ENTRY_LAST_ACCESS_TIME_OFFSET = 32;
final static int INDEX_ITEM_MAP_ENTRY_TIME_TO_LIVE_OFFSET = 40;
final static int INDEX_ITEM_MAP_ENTRY_STATUS = 60;
private IMappedPage indexPage;
private IMappedPageFactory dataPageFactory;
private long index;
private int indexItemOffset;
private int realLength;
public MapEntry(long index, int realLength, int indexItemOffset, IMappedPage indexPage, IMappedPageFactory dataPageFactory) {
this.index = index;
this.realLength = realLength;
this.indexItemOffset = indexItemOffset;
this.indexPage = indexPage;
this.dataPageFactory = dataPageFactory;
}
public MapEntry(long index, int indexItemOffset, IMappedPage indexPage, IMappedPageFactory dataPageFactory) {
this.index = index;
this.indexItemOffset = indexItemOffset;
this.indexPage = indexPage;
this.dataPageFactory = dataPageFactory;
this.realLength = this.getKeyLength() + this.getValueLength();
}
public long getIndex() {
return this.index;
}
/**
* key length + value length
* @return length
*/
public int getRealEntryLength() {
return this.realLength;
}
public int getSlotSize() {
return indexPage.getLocal().getInt(indexItemOffset + INDEX_ITEM_DATA_SLOT_LENGTH_OFFSET);
}
public int getKeyLength() {
return indexPage.getLocal().getInt(indexItemOffset + INDEX_ITEM_MAP_ENTRY_KEY_LENGTH_OFFSET);
}
public void putKeyLength(int keyLength) {
indexPage.getLocal().putInt(indexItemOffset + INDEX_ITEM_MAP_ENTRY_KEY_LENGTH_OFFSET, keyLength);
indexPage.setDirty(true);
}
public int getValueLength() {
return indexPage.getLocal().getInt(indexItemOffset + INDEX_ITEM_MAP_ENTRY_VALUE_LENGTH_OFFSET);
}
public void putValueLength(int valueLength) {
indexPage.getLocal().putInt(indexItemOffset + INDEX_ITEM_MAP_ENTRY_VALUE_LENGTH_OFFSET, valueLength);
indexPage.setDirty(true);
}
public long getCreatedTime() {
return indexPage.getLocal().getLong(indexItemOffset + INDEX_ITEM_MAP_ENTRY_CREATED_TIME_OFFSET);
}
public void putCreatedTime(long createdTime) {
indexPage.getLocal().putLong(indexItemOffset + INDEX_ITEM_MAP_ENTRY_CREATED_TIME_OFFSET, createdTime);
indexPage.setDirty(true);
}
public long getLastAccessedTime() {
return indexPage.getLocal().getLong(indexItemOffset + INDEX_ITEM_MAP_ENTRY_LAST_ACCESS_TIME_OFFSET);
}
public void putLastAccessedTime(long lastAccessedTime) {
indexPage.getLocal().putLong(indexItemOffset + INDEX_ITEM_MAP_ENTRY_LAST_ACCESS_TIME_OFFSET, lastAccessedTime);
indexPage.setDirty(true);
}
public long getTimeToLive() {
return indexPage.getLocal().getLong(indexItemOffset + INDEX_ITEM_MAP_ENTRY_TIME_TO_LIVE_OFFSET);
}
public void putTimeToLive(long ttlInMs) {
indexPage.getLocal().putLong(indexItemOffset + INDEX_ITEM_MAP_ENTRY_TIME_TO_LIVE_OFFSET, ttlInMs);
indexPage.setDirty(true);
}
public boolean isReleased() {
byte status = indexPage.getLocal().get(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS);
return (status & (1 << 1)) != 0;
}
public void markReleased() {
byte status = indexPage.getLocal().get(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS);
status = (byte) (status | ( 1 << 1));
indexPage.getLocal().put(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS, status);
indexPage.setDirty(true);
}
public boolean isInUse() {
byte status = indexPage.getLocal().get(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS);
return (status & (1 << 1)) == 0;
}
public void MarkInUse() {
byte status = indexPage.getLocal().get(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS);
status = (byte) (status & ~( 1 << 1));
indexPage.getLocal().put(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS, status);
indexPage.setDirty(true);
}
public boolean isAllocated() {
byte status = indexPage.getLocal().get(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS);
return (status & 1) != 0;
}
public void MarkAllocated() {
byte status = indexPage.getLocal().get(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS);
status = (byte) (status | 1);
indexPage.getLocal().put(indexItemOffset + INDEX_ITEM_MAP_ENTRY_STATUS, status);
indexPage.setDirty(true);
}
public byte[] getEntryKey() throws IOException {
long dataPageIndex = indexPage.getLocal().getLong(indexItemOffset + INDEX_ITEM_DATA_PAGE_INDEX_OFFSET);
int dataSlotOffset = indexPage.getLocal().getInt(indexItemOffset + INDEX_ITEM_DATA_SLOT_OFFSET_OFFSET);
int entryKeyOffset = dataSlotOffset;
int entryKeyLength = this.getKeyLength();
IMappedPage dataPage = dataPageFactory.acquirePage(dataPageIndex);
byte[] value = dataPage.getLocal(entryKeyOffset, entryKeyLength);
return value;
}
public void putEntryKey(byte[] entryKey) throws IOException {
long dataPageIndex = indexPage.getLocal().getLong(indexItemOffset + INDEX_ITEM_DATA_PAGE_INDEX_OFFSET);
int dataSlotOffset = indexPage.getLocal().getInt(indexItemOffset + INDEX_ITEM_DATA_SLOT_OFFSET_OFFSET);
int entryKeyOffset = dataSlotOffset;
IMappedPage dataPage = dataPageFactory.acquirePage(dataPageIndex);
ByteBuffer dataItemBuffer = dataPage.getLocal(entryKeyOffset);
dataItemBuffer.put(entryKey);
dataPage.setDirty(true);
}
public byte[] getEntryValue() throws IOException {
long dataPageIndex = indexPage.getLocal().getLong(indexItemOffset + INDEX_ITEM_DATA_PAGE_INDEX_OFFSET);
int dataSlotOffset = indexPage.getLocal().getInt(indexItemOffset + INDEX_ITEM_DATA_SLOT_OFFSET_OFFSET);
int entryValueOffset = dataSlotOffset + this.getKeyLength();
int entryValueLength = getValueLength();
IMappedPage dataPage = dataPageFactory.acquirePage(dataPageIndex);
byte[] value = dataPage.getLocal(entryValueOffset, entryValueLength);
return value;
}
public void putEntryValue(byte[] entryValue) throws IOException {
long dataPageIndex = indexPage.getLocal().getLong(indexItemOffset + INDEX_ITEM_DATA_PAGE_INDEX_OFFSET);
int dataSlotOffset = indexPage.getLocal().getInt(indexItemOffset + INDEX_ITEM_DATA_SLOT_OFFSET_OFFSET);
int entryValueOffset = dataSlotOffset + this.getKeyLength();
IMappedPage dataPage = dataPageFactory.acquirePage(dataPageIndex);
ByteBuffer dataItemBuffer = dataPage.getLocal(entryValueOffset);
dataItemBuffer.put(entryValue);
dataPage.setDirty(true);
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (o == this) return true;
if (getClass() != o.getClass()) return false;
MapEntry other = (MapEntry)o;
try {
// equals only if the keys are equal
return Arrays.equals(this.getEntryKey(), other.getEntryKey());
} catch (IOException e) {
throw new RuntimeException("fail to get entry key", e);
}
}
}