}
PersistitException verify(Key key, final VerifyVisitor visitor) {
try {
if (_page == 0) {
return new InvalidPageStructureException("head page is neither a data page nor an index page");
}
if (!isIndexPage() && !isDataPage()) {
return new InvalidPageStructureException("page type " + _type
+ " is neither data page nor an index page");
}
if (key == null) {
key = new Key(_persistit);
}
final byte[] kb = key.getEncodedBytes();
final int[] plan = getRepackPlanBuffer();
for (int index = 0; index < plan.length; index++) {
plan[index] = 0;
}
if (visitor != null) {
visitor.visitPage(getTimestamp(), getVolume(), getPageAddress(), getPageType(), getBufferSize(),
getKeyBlockStart(), getKeyBlockEnd(), getAlloc(), getAvailableSize(), getRightSibling());
}
for (int p = KEY_BLOCK_START; p < _keyBlockEnd; p += KEYBLOCK_LENGTH) {
final int kbData = getInt(p);
final int db = decodeKeyBlockDb(kbData);
final int ebc = decodeKeyBlockEbc(kbData);
final int tail = decodeKeyBlockTail(kbData);
if (p == KEY_BLOCK_START && ebc != 0) {
return new InvalidPageStructureException("invalid initial ebc " + ebc + " for keyblock at " + p
+ " --[" + summarize() + "]");
}
if (tail < _keyBlockEnd || tail < _alloc || tail > _bufferSize - _tailHeaderSize
|| (tail & ~TAILBLOCK_MASK) != 0) {
return new InvalidPageStructureException("invalid tail block offset " + tail + " for keyblock at "
+ p + " --[" + summarize() + "]");
}
final int tbData = getInt(tail);
final int klength = decodeTailBlockKLength(tbData);
if ((tbData & TAILBLOCK_INUSE_MASK) == 0) {
return new InvalidPageStructureException("not in-use tail block offset " + tail
+ " for keyblock at " + p + " --[" + summarize() + "]");
}
// Verify that first key in this pages matches the final key
// of the preceding page.
if (p == KEY_BLOCK_START && key.getEncodedSize() != 0) {
int index = 0;
int compare = 0;
int size = key.getEncodedSize();
if (klength < size)
size = klength + 1;
compare = (kb[0] & 0xFF) - db;
while (compare == 0 && ++index < size) {
compare = (kb[index] & 0xFF) - (_bytes[tail + _tailHeaderSize + index - 1] & 0xFF);
}
if (compare != 0) {
final String s = compare < 0 ? "too big" : "too small";
return new InvalidPageStructureException("initial key " + s + " at offset " + index
+ " for keyblock at " + p + " --[" + summarize() + "]");
}
}
// Verify that successor keys follow in sequence.
if (p > KEY_BLOCK_START && ebc < key.getEncodedSize()) {
final int dbPrev = kb[ebc] & 0xFF;
if (db < dbPrev) {
return new InvalidPageStructureException("db not greater: db=" + db + " dbPrev=" + dbPrev
+ " for keyblock at " + p + " --[" + summarize() + "]");
}
}
//
// If this is an index page, make sure the pointer isn't
// redundant
//
if (isIndexPage()) {
final int pointer = getInt(tail + 4);
if (visitor != null) {
visitor.visitIndexRecord(key, p, tail, klength, pointer);
}
if (pointer == -1) {
if (p + KEYBLOCK_LENGTH != _keyBlockEnd) {
return new InvalidPageStructureException("index pointer has pointer to -1 "
+ " for keyblock at " + p + " --[" + summarize() + "]");
}
}
} else if (isDataPage()) {
final int size = decodeTailBlockSize(tbData);
final int offset = tail + _tailHeaderSize + klength;
final int length = size - klength - _tailHeaderSize;
if (visitor != null) {
visitor.visitDataRecord(key, p, tail, klength, offset, length, getBytes());
}
if (!MVV.verify(_bytes, offset, length)) {
throw new InvalidPageStructureException("invalid MVV record at offset/length=" + offset + "/"
+ length);
}
}
if (_pool != null && getKeyCount() > _pool.getMaxKeys()) {
return new InvalidPageStructureException("page has too many keys: has " + getKeyCount()
+ " but max is " + _pool.getMaxKeys());
}
kb[ebc] = (byte) db;
System.arraycopy(_bytes, tail + _tailHeaderSize, kb, ebc + 1, klength);
key.setEncodedSize(ebc + klength + 1);
plan[tail / TAILBLOCK_FACTOR] = p;
}
// Now check the free blocks
final int formerBlock = _alloc;
for (int tail = _alloc; tail < _bufferSize;) {
if ((tail & ~TAILBLOCK_MASK) != 0 || tail < 0 || tail > _bufferSize) {
return new InvalidPageStructureException("Tail block at " + formerBlock + " is invalid");
}
final int tbData = getInt(tail);
final int size = decodeTailBlockSize(tbData);
if (size <= ~TAILBLOCK_MASK || size >= _bufferSize - _keyBlockEnd) {
return new InvalidPageStructureException("Tailblock at " + tail + " has invalid size=" + size);
}
if ((tbData & TAILBLOCK_INUSE_MASK) != 0) {
if (plan[tail / TAILBLOCK_FACTOR] == 0) {
return new InvalidPageStructureException("Tailblock at " + tail + " is in use, but no key "
+ " block points to it.");
}
final int klength = decodeTailBlockKLength(tbData);
{
if (klength + _tailHeaderSize > size) {
return new InvalidPageStructureException("Tailblock at " + tail + " has klength=" + klength
+ " longer than size=" + size + " - headerSize=" + _tailHeaderSize);
}
}
} else {
if (plan[tail / TAILBLOCK_FACTOR] != 0) {
return new InvalidPageStructureException("Tailblock at " + tail + " is marked free, but the "
+ " key block at " + plan[tail / TAILBLOCK_FACTOR] + " points to it.");
}
}
tail += ((size + ~TAILBLOCK_MASK) & TAILBLOCK_MASK);
}