package org.basex.build;
import static org.basex.data.DataText.*;
import java.io.IOException;
import org.basex.core.Context;
import org.basex.core.MainProp;
import org.basex.core.cmd.DropDB;
import org.basex.data.Data;
import org.basex.data.DiskData;
import org.basex.data.MetaData;
import org.basex.index.Names;
import org.basex.io.IO;
import org.basex.io.in.DataInput;
import org.basex.io.out.DataOutput;
import org.basex.io.out.TableOutput;
import org.basex.io.random.TableAccess;
import org.basex.io.random.TableDiskAccess;
import org.basex.util.Compress;
import org.basex.util.Token;
import org.basex.util.Util;
/**
* This class creates a database instance on disk.
* The storage layout is described in the {@link Data} class.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class DiskBuilder extends Builder {
/** Database table. */
private DataOutput tout;
/** Database texts. */
private DataOutput xout;
/** Database values. */
private DataOutput vout;
/** Output stream for temporary values. */
private DataOutput sout;
/** Admin properties. */
private final MainProp mprop;
/** Text compressor. */
private final Compress comp;
/**
* Constructor.
* @param nm name of database
* @param parse parser
* @param ctx database context
*/
public DiskBuilder(final String nm, final Parser parse, final Context ctx) {
super(nm, parse, ctx.prop);
comp = new Compress();
mprop = ctx.mprop;
}
@Override
public DiskData build() throws IOException {
DropDB.drop(name, mprop);
mprop.dbpath(name).md();
final IO file = parser.src;
final MetaData md = new MetaData(name, prop, mprop);
md.original = file != null ? file.path() : "";
md.filesize = file != null ? file.length() : 0;
md.time = file != null ? file.timeStamp() : System.currentTimeMillis();
md.dirty = true;
// calculate optimized output buffer sizes to reduce disk fragmentation
final Runtime rt = Runtime.getRuntime();
int bs = (int) Math.min(md.filesize, Math.min(1 << 22,
rt.maxMemory() - rt.freeMemory() >> 2));
bs = Math.max(IO.BLOCKSIZE, bs - bs % IO.BLOCKSIZE);
tout = new DataOutput(new TableOutput(md, DATATBL));
xout = new DataOutput(md.dbfile(DATATXT), bs);
vout = new DataOutput(md.dbfile(DATAATV), bs);
sout = new DataOutput(md.dbfile(DATATMP), bs);
final Names tags = new Names(md);
final Names atts = new Names(md);
parse(md, tags, atts);
close();
// copy temporary values into database table
final TableAccess ta = new TableDiskAccess(md, DATATBL);
final DataInput in = new DataInput(md.dbfile(DATATMP));
for(; spos < ssize; ++spos) ta.write4(in.readNum(), 8, in.readNum());
ta.close();
in.close();
md.dbfile(DATATMP).delete();
// return database instance
return new DiskData(md, tags, atts, path, ns);
}
@Override
public void abort() {
try {
close();
} catch(final IOException ex) {
Util.debug(ex);
}
DropDB.drop(meta.name, mprop);
}
@Override
public void close() throws IOException {
if(tout != null) tout.close();
if(xout != null) xout.close();
if(vout != null) vout.close();
if(sout != null) sout.close();
parser.close();
tout = null;
xout = null;
vout = null;
sout = null;
}
@Override
protected void addDoc(final byte[] value) throws IOException {
tout.write1(Data.DOC);
tout.write2(0);
tout.write5(textOff(value, true));
tout.write4(0);
tout.write4(meta.size++);
}
@Override
protected void addElem(final int dist, final int nm, final int asize,
final int uri, final boolean ne) throws IOException {
tout.write1(asize << 3 | Data.ELEM);
tout.write2((ne ? 1 << 15 : 0) | nm);
tout.write1(uri);
tout.write4(dist);
tout.write4(asize);
tout.write4(meta.size++);
}
@Override
protected void addAttr(final int nm, final byte[] value, final int dist,
final int uri) throws IOException {
tout.write1(dist << 3 | Data.ATTR);
tout.write2(nm);
tout.write5(textOff(value, false));
tout.write4(uri);
tout.write4(meta.size++);
}
@Override
protected void addText(final byte[] value, final int dist, final byte kind)
throws IOException {
tout.write1(kind);
tout.write2(0);
tout.write5(textOff(value, true));
tout.write4(dist);
tout.write4(meta.size++);
}
@Override
protected void setSize(final int pre, final int size) throws IOException {
sout.writeNum(pre);
sout.writeNum(size);
++ssize;
}
/**
* Calculates the text offset and writes the text value.
* @param value value to be inlined
* @param text text/attribute flag
* @return inline value or text position
* @throws IOException I/O exception
*/
private long textOff(final byte[] value, final boolean text)
throws IOException {
// inline integer values...
final long v = Token.toSimpleInt(value);
if(v != Integer.MIN_VALUE) return v | IO.OFFNUM;
// store text
final DataOutput store = text ? xout : vout;
final long off = store.size();
final byte[] val = comp.pack(value);
store.writeToken(val);
return val == value ? off : off | IO.OFFCOMP;
}
}