package org.infinispan.persistence.sifs;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* Helper for reading/writing entries into file.
*
* @author Radim Vansa <rvansa@redhat.com>
*/
class EntryRecord {
private EntryHeader header;
private byte[] key;
private byte[] metadata;
private byte[] value;
public EntryRecord(EntryHeader header, byte[] key, byte[] metadata, byte[] value) {
this.header = header;
this.key = key;
this.metadata = metadata;
this.value = value;
}
public EntryHeader getHeader() {
return header;
}
public byte[] getKey() {
return key;
}
public byte[] getMetadata() {
return metadata;
}
public byte[] getValue() {
return value;
}
public EntryRecord loadMetadataAndValue(FileProvider.Handle handle, int offset) throws IOException {
int metadataOffset = offset + EntryHeader.HEADER_SIZE + header.keyLength();
if (header.metadataLength() > 0) {
metadata = new byte[header.metadataLength()];
if (read(handle, ByteBuffer.wrap(metadata), metadataOffset, header.metadataLength()) < 0) {
throw new IllegalStateException("End of file reached when reading metadata on "
+ handle.getFileId() + ":" + offset + ": " + header);
}
}
assert header.valueLength() > 0;
value = new byte[header.valueLength()];
if (read(handle, ByteBuffer.wrap(value), metadataOffset + header.metadataLength(), header.valueLength()) < 0) {
throw new IllegalStateException("End of file reached when reading value on "
+ handle.getFileId() + ":" + offset + ": " + header);
}
return this;
}
public static EntryHeader readEntryHeader(FileProvider.Handle handle, long offset) throws IOException {
ByteBuffer header = ByteBuffer.allocate(EntryHeader.HEADER_SIZE);
if (read(handle, header, offset, EntryHeader.HEADER_SIZE) < 0) {
return null;
}
header.flip();
try {
return new EntryHeader(header);
} catch (IllegalStateException e) {
throw new IllegalStateException("Error reading from " + handle.getFileId() + ":" + offset);
}
}
public static byte[] readKey(FileProvider.Handle handle, EntryHeader header, long offset) throws IOException {
byte[] key = new byte[header.keyLength()];
if (read(handle, ByteBuffer.wrap(key), offset + EntryHeader.HEADER_SIZE, header.keyLength()) < 0) {
throw new IllegalStateException("End of file reached when reading key on "
+ handle.getFileId() + ":" + offset);
}
return key;
}
public static byte[] readMetadata(FileProvider.Handle handle, EntryHeader header, long offset) throws IOException {
assert header.metadataLength() > 0;
byte[] metadata = new byte[header.metadataLength()];
if (read(handle, ByteBuffer.wrap(metadata), offset + EntryHeader.HEADER_SIZE + header.keyLength(), header.metadataLength()) < 0) {
throw new IllegalStateException("End of file reached when reading metadata on "
+ handle.getFileId() + ":" + offset + ": " + header);
}
return metadata;
}
public static byte[] readValue(FileProvider.Handle handle, EntryHeader header, long offset) throws IOException {
assert header.valueLength() > 0;
byte[] value = new byte[header.valueLength()];
if (read(handle, ByteBuffer.wrap(value), offset + EntryHeader.HEADER_SIZE + header.keyLength() + header.metadataLength(), header.valueLength()) < 0) {
throw new IllegalStateException("End of file reached when reading metadata on "
+ handle.getFileId() + ":" + offset + ": " + header);
}
return value;
}
private static int read(FileProvider.Handle handle, ByteBuffer buffer, long position, int length) throws IOException {
int read = 0;
do {
int newRead = handle.read(buffer, position + read);
if (newRead < 0) {
return -1;
}
read += newRead;
} while (read < length);
return read;
}
private static boolean read(FileChannel fileChannel, ByteBuffer buffer, int length) throws IOException {
int read = 0;
do {
int newRead = fileChannel.read(buffer);
if (newRead < 0) {
return false;
}
read += newRead;
} while (read < length);
return true;
}
public static void writeEntry(FileChannel fileChannel, byte[] serializedKey, byte[] serializedMetadata, byte[] serializedValue, long seqId, long expiration) throws IOException {
ByteBuffer header = ByteBuffer.allocate(EntryHeader.HEADER_SIZE);
if (EntryHeader.useMagic) {
header.putInt(EntryHeader.MAGIC);
}
header.putShort((short) serializedKey.length);
header.putShort(serializedMetadata == null ? (short) 0 : (short) serializedMetadata.length);
header.putInt(serializedValue == null ? 0 : serializedValue.length);
header.putLong(seqId);
header.putLong(expiration);
header.flip();
write(fileChannel, header);
write(fileChannel, ByteBuffer.wrap(serializedKey));
if (serializedMetadata != null) {
write(fileChannel, ByteBuffer.wrap(serializedMetadata));
}
if (serializedValue != null) {
write(fileChannel, ByteBuffer.wrap(serializedValue));
}
}
public static void writeEntry(FileChannel fileChannel, org.infinispan.commons.io.ByteBuffer serializedKey, org.infinispan.commons.io.ByteBuffer serializedMetadata, org.infinispan.commons.io.ByteBuffer serializedValue, long seqId, long expiration) throws IOException {
ByteBuffer header = ByteBuffer.allocate(EntryHeader.HEADER_SIZE);
if (EntryHeader.useMagic) {
header.putInt(EntryHeader.MAGIC);
}
header.putShort((short) serializedKey.getLength());
header.putShort(serializedMetadata == null ? (short) 0 : (short) serializedMetadata.getLength());
header.putInt(serializedValue == null ? 0 : serializedValue.getLength());
header.putLong(seqId);
header.putLong(expiration);
header.flip();
write(fileChannel, header);
write(fileChannel, ByteBuffer.wrap(serializedKey.getBuf(), serializedKey.getOffset(), serializedKey.getLength()));
if (serializedMetadata != null) {
write(fileChannel, ByteBuffer.wrap(serializedMetadata.getBuf(), serializedMetadata.getOffset(), serializedMetadata.getLength()));
}
if (serializedValue != null) {
write(fileChannel, ByteBuffer.wrap(serializedValue.getBuf(), serializedValue.getOffset(), serializedValue.getLength()));
}
}
private static void write(FileChannel fileChannel, ByteBuffer buffer) throws IOException {
while (buffer.hasRemaining()) fileChannel.write(buffer);
}
}