package at.jku.sii.sqlitereader.btree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import at.jku.sii.sqlitereader.SqliteDataBase;
import at.jku.sii.sqlitereader.io.AdvancedDataInput.Varint;
import at.jku.sii.sqlitereader.io.ArrayDataInput;
import at.jku.sii.sqlitereader.io.ChainedArrayDataInput;
import at.jku.sii.sqlitereader.page.PayloadOverflowPage;
import at.jku.sii.sqlitereader.record.Record;
public abstract class PayloadCell implements Cell {
private final long numPayloadBytes;
private final ArrayDataInput payload;
private final int overflowPage;
private final List<PayloadOverflowPage> overflowPages;
private Record record;
PayloadCell(Varint numPayloadBytes, Type type, ArrayDataInput in, SqliteDataBase db) {
this.numPayloadBytes = numPayloadBytes.value;
int payloadSize = getInternalPayloadSize(type, db, this.numPayloadBytes);
byte[] data = new byte[payloadSize];
in.readFully(data);
this.payload = new ArrayDataInput(data, in.createLastSubAnnotator(payloadSize));
in.annotateLast(payloadSize, "Payload");
if (payloadSize < this.numPayloadBytes) {
this.overflowPage = in.readInt("overflowPage");
this.overflowPages = new ArrayList<PayloadOverflowPage>();
} else {
this.overflowPage = -1;
this.overflowPages = Collections.emptyList();
}
}
protected static int getInternalPayloadSize(Type type, SqliteDataBase db, long numPayloadBytes) {
long p = numPayloadBytes;
int u = db.getUsableSize();
long m = ((u - 12) * 32 / 255) - 23;
switch (type) {
case TABLE_LEAF:
if (p <= u - 35) { // everything in cell
return (int) p;
} else {
return (int) (Math.min(m + ((p - m) % (u - 4)), u - 35));
}
case TABLE_INTERIOR:
return 0; // no payload
case INDEX_INTERIOR:
case INDEX_LEAF:
long x = ((u - 12) * 64 / 255) - 23;
if (p <= x)
return (int) p;
else {
return (int) (Math.min(m + ((p - m) % (u - 4)), x));
}
}
throw new IllegalStateException();
}
public final void resolveOverflowPages(SqliteDataBase db) {
if (this.overflowPage <= 0)
return;
this.overflowPages.addAll(PayloadOverflowPage.readOverflowPages(db, this.overflowPage));
}
public final long getNumPlayloadBytes() {
return this.numPayloadBytes;
}
public final Record getRecord() {
return this.record;
}
private boolean resolvedOveflowPages() {
return this.overflowPage <= 0 || !this.overflowPages.isEmpty();
}
public final void readRecords(SqliteDataBase db) {
assert (this.resolvedOveflowPages());
List<ArrayDataInput> l = new ArrayList<ArrayDataInput>();
l.add(this.payload);
for (PayloadOverflowPage o : this.overflowPages)
l.add(o.getData());
final ChainedArrayDataInput in = new ChainedArrayDataInput(l);
this.record = Record.read(in, db.getEncoding());
in.annotateLast((int) in.currentPosition(), "Record");
// TODO clean up?
}
@Override
public String toString() {
return String.format("PayloadCell [%s]", this.toStringAttr());
}
protected final String toStringAttr() {
return String.format("numPayloadBytes=%s, overflowPage=%s, overflowPages=%s", this.numPayloadBytes, this.overflowPage, this.overflowPages);
}
@Override
public void dump(StringBuilder b) {
b.append(this.toString());
}
}