package org.opendedup.sdfs.filestore; import java.io.IOException; import java.nio.ByteBuffer; import org.bouncycastle.util.Arrays; import org.opendedup.sdfs.Main; import org.opendedup.util.HashFunctions; /** * * @author sam silverberg Chunk block meta data is as follows: [mark of deletion * (1 byte)|hash lenth(2 bytes)|hash(32 bytes)|date added (8 bytes)|date * last claimed (8 bytes)| number of times claimed (8 bytes)|chunk len * (4 bytes)|chunk position (8 bytes)] * */ public class ChunkData { public static final int RAWDL = 1 + 2 + 32 + 8 + 8 + 8 + 4 + 8; public static final int CLAIMED_OFFSET = 1 + 2 + 32 + 8; private static byte [] BLANKCM = new byte[RAWDL]; private boolean mDelete = false; private short hashLen = 0; private byte[] hash = null; private long added = 0; private long lastClaimed = 0; private long numClaimed = 0; private int cLen = 0; private long cPos = 0; private byte[] chunk = null; private static AbstractChunkStore fileStore = new FileChunkStore("chunks"); private static byte [] blankHash = null;; static { Arrays.fill(BLANKCM, (byte) 0); try { blankHash = HashFunctions.getTigerHashBytes(new byte[Main.chunkStorePageSize]); }catch(Exception e) { e.printStackTrace(); } } public ChunkData(long cPos) { this.cPos = cPos; this.mDelete = true; } public ChunkData(byte[] rawData) { ByteBuffer buf = ByteBuffer.wrap(rawData); byte del = buf.get(); if (del == 0) this.mDelete = false; else this.mDelete = true; this.hashLen = buf.getShort(); this.hash = new byte[hashLen]; buf.get(hash); buf.get(new byte[32 - hashLen]); added = buf.getLong(); lastClaimed = buf.getLong(); this.numClaimed = buf.getLong(); cLen = buf.getInt(); cPos = buf.getLong(); } public ChunkData(byte[] hash, int chunkLen, byte[] chunk) { long tm = System.currentTimeMillis(); this.added = tm; this.lastClaimed = tm; this.numClaimed = 1; this.mDelete = false; this.hashLen = (short) hash.length; this.hash = hash; this.cLen = chunkLen; this.cPos = -1; this.chunk = chunk; } public ByteBuffer getMetaDataBytes() { ByteBuffer buf = ByteBuffer.allocateDirect(RAWDL); if (this.mDelete) { buf.put(BLANKCM); buf.position(0); return buf; } else { buf.put((byte) 0); buf.putShort(this.hashLen); buf.put(hash); buf.put(new byte[32 - this.hashLen]); buf.putLong(this.added); buf.putLong(this.lastClaimed); buf.putLong(this.numClaimed); buf.putInt(this.cLen); buf.putLong(cPos); buf.position(0); return buf; } } public void persistData(boolean clear) throws IOException { if (cPos == -1) { this.cPos = fileStore.reserveWritePosition(cLen); } if (this.mDelete) { chunk = new byte[cLen]; } fileStore.writeChunk(hash, chunk, cLen, cPos); if(clear) this.chunk = null; } public boolean ismDelete() { return mDelete; } public void setmDelete(boolean mDelete) { this.mDelete = mDelete; } public long getLastClaimed() { return lastClaimed; } public void setLastClaimed(long lastClaimed) { this.lastClaimed = lastClaimed; } public long getNumClaimed() { return this.numClaimed; } public void updateNumClaimed(int num) { this.numClaimed += num; } public long getcPos() { return cPos; } public void setcPos(long cPos) { this.cPos = cPos; } public short getHashLen() { return hashLen; } public byte[] getHash() { return hash; } public static byte[] getChunk(byte[] hash, long pos) throws IOException { try { return fileStore.getChunk(hash, pos, Main.chunkStorePageSize); } catch(IOException e) { if(Arrays.areEqual(hash, blankHash)) return new byte[Main.chunkStorePageSize]; else throw e; } } public byte[] getData() throws IOException { if (this.chunk == null) { return fileStore.getChunk(hash, this.cPos, this.cLen); } else return chunk; } public long getAdded() { return added; } public int getcLen() { return cLen; } }