final TreeDescriptor td = _handleToTreeMap.get(treeHandle);
final int volumeHandle = td.getVolumeHandle();
long page = Buffer.decodeLongRecordDescriptorPointer(value.getEncodedBytes(), 0);
final int size = Buffer.decodeLongRecordDescriptorSize(value.getEncodedBytes(), 0);
if (size < 0 || size > Value.MAXIMUM_SIZE) {
throw new CorruptJournalException("Transactional long record specification " + "exceeds maximum size of "
+ Value.MAXIMUM_SIZE + ":" + size);
}
final byte[] rawBytes = value.getEncodedBytes();
final long startAddress = page;
value.clear();
if (size > value.getMaximumSize()) {
value.setMaximumSize(size);
}
value.ensureFit(size);
int offset = 0; // offset of next segment in the value
int remainingSize = size;
System.arraycopy(rawBytes, Buffer.LONGREC_PREFIX_OFFSET, value.getEncodedBytes(), offset,
Buffer.LONGREC_PREFIX_SIZE);
offset += Buffer.LONGREC_PREFIX_SIZE;
remainingSize -= Buffer.LONGREC_PREFIX_SIZE;
for (int count = 0; page != 0; count++) {
if (remainingSize == 0) {
throw new CorruptJournalException("Long record chain has more than " + size
+ " bytes starting at page " + startAddress + " for transaction at "
+ addressToString(from, timestamp));
}
//
// Look for the latest version of the page which precedes the
// record's timestamp.
//
final PageNode key = new PageNode(volumeHandle, page, -1, -1);
PageNode pn = lastPageNodeBefore(_branchMap.get(key), timestamp);
if (pn == null) {
pn = lastPageNodeBefore(_pageMap.get(key), timestamp);
}
if (pn == null) {
throw new CorruptJournalException("Long record chain missing page " + page + " at count " + count
+ " at " + addressToString(from, timestamp));
}
_currentAddress = pn.getJournalAddress();
read(_currentAddress, PA.OVERHEAD);
final int type = PA.getType(_readBuffer);
final int recordSize = PA.getLength(_readBuffer);
final int payloadSize = recordSize - PA.OVERHEAD;
final int leftSize = PA.getLeftSize(_readBuffer);
final int bufferSize = PA.getBufferSize(_readBuffer);
final long pageAddress = PA.getPageAddress(_readBuffer);
//
// Verify that this is the valid and appropriate PA record
//
if (type != PA.TYPE) {
throw new CorruptJournalException("Record at " + pn.toStringJournalAddress(this)
+ " is not a PAGE record");
}
if (leftSize < 0 || payloadSize < leftSize || payloadSize > bufferSize) {
throw new CorruptJournalException("Record at " + pn.toStringJournalAddress(this)
+ " invalid sizes: recordSize= " + payloadSize + " leftSize=" + leftSize + " bufferSize="
+ bufferSize);
}
if (pageAddress != pn.getPageAddress()) {
throw new CorruptJournalException("Record at " + pn.toStringJournalAddress(this)
+ " mismatched page address: expected/actual=" + pn.getPageAddress() + "/" + pageAddress);
}
//
// Verify that this is a PAGE_TYPE_LONG_RECORD
//
read(_currentAddress, recordSize);
final int pageType = JournalRecord.getByte(_readBuffer, PA.OVERHEAD + Buffer.TYPE_OFFSET);
if (pageType != Buffer.PAGE_TYPE_LONG_RECORD) {
throw new CorruptJournalException("Long record chain contains invalid page type " + pageType
+ " for page " + page + " at " + pn.toStringJournalAddress(this) + " in transaction at "
+ addressToString(from, timestamp));
}
final int segmentSize = Math.min(remainingSize, payloadSize - Buffer.HEADER_SIZE);
System.arraycopy(_readBuffer.array(), _readBuffer.position() + PA.OVERHEAD + Buffer.HEADER_SIZE,
value.getEncodedBytes(), offset, segmentSize);
offset += segmentSize;
remainingSize -= segmentSize;
// Next page in chain
page = JournalRecord.getLong(_readBuffer, PA.OVERHEAD + Buffer.RIGHT_SIBLING_OFFSET);
if (count > Buffer.MAX_LONG_RECORD_CHAIN) {
throw new CorruptJournalException("Long record chain has more than " + Buffer.MAX_LONG_RECORD_CHAIN
+ " pages in starting at page " + startAddress + " for transaction at "
+ addressToString(from, timestamp));
}
}
if (remainingSize != 0) {
throw new CorruptJournalException("Long record chain has fewer than " + size + " bytes (" + remainingSize
+ " not recovered) starting at page " + startAddress + " for transaction at "
+ addressToString(from, timestamp));
}
value.setEncodedSize(size);
}