package com.ctriposs.sdb.table; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; /** * File channel map entry implementation * * @author bulldog * */ public class FCMapEntryImpl implements IMapEntry { private int index; private FileChannel dataChannel; // index cached with memory mapped file private MappedByteBuffer indexMappedByteBuffer; // cache private byte[] key; private byte[] value; public FCMapEntryImpl(int index, MappedByteBuffer indexMappedByteBuffer, FileChannel dataChannel) { this.index = index; this.dataChannel = dataChannel; this.indexMappedByteBuffer = indexMappedByteBuffer; } long getItemOffsetInDataFile() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index; return this.indexMappedByteBuffer.getLong(offsetInIndexFile); } private int getKeyLength() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_KEY_LENGTH_OFFSET; return this.indexMappedByteBuffer.getInt(offsetInIndexFile); } @Override public int getKeyHash() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_KEY_HASH_CODE_OFFSET; int hashCode = this.indexMappedByteBuffer.getInt(offsetInIndexFile); return hashCode; } private int getValueLength() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_VALUE_LENGTH_OFFSET; return this.indexMappedByteBuffer.getInt(offsetInIndexFile); } @Override public byte[] getKey() throws IOException { if (key != null) return key; long itemOffsetInDataFile = this.getItemOffsetInDataFile(); int keyLength = this.getKeyLength(); ByteBuffer keyBuf = ByteBuffer.allocate(keyLength); this.dataChannel.read(keyBuf, itemOffsetInDataFile); key = keyBuf.array(); return key; } @Override public byte[] getValue() throws IOException { if (value != null) return value; long itemOffsetInDataFile = this.getItemOffsetInDataFile(); int keyLength = this.getKeyLength(); int valueLength = this.getValueLength(); ByteBuffer valueBuf = ByteBuffer.allocate(valueLength); this.dataChannel.read(valueBuf, itemOffsetInDataFile + keyLength); value = valueBuf.array(); return value; } @Override public long getTimeToLive() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_TIME_TO_LIVE_OFFSET; return this.indexMappedByteBuffer.getLong(offsetInIndexFile); } @Override public long getCreatedTime() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_CREATED_TIME_OFFSET; return this.indexMappedByteBuffer.getLong(offsetInIndexFile); } @Override public boolean isDeleted() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_STATUS; byte status = this.indexMappedByteBuffer.get(offsetInIndexFile); return (status & ( 1 << 1)) != 0; } @Override public void markDeleted() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_STATUS; byte status = this.indexMappedByteBuffer.get(offsetInIndexFile); status = (byte) (status | 1 << 1); this.indexMappedByteBuffer.put((int)offsetInIndexFile, status); } @Override public boolean isExpired() throws IOException { long ttl = this.getTimeToLive(); if (ttl > 0) { if (System.currentTimeMillis() - this.getCreatedTime() > ttl) return true; } return false; } @Override public boolean isInUse() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_STATUS; byte status = this.indexMappedByteBuffer.get(offsetInIndexFile); return (status & 1) != 0; } @Override public int getIndex() { return this.index; } @Override public boolean isCompressed() throws IOException { int offsetInIndexFile = AbstractMapTable.INDEX_ITEM_LENGTH * index + IMapEntry.INDEX_ITEM_STATUS; byte status = this.indexMappedByteBuffer.get(offsetInIndexFile); return (status & ( 1 << 2)) != 0; } }