package org.basex.build;
import static org.basex.data.DataText.*;
import java.io.*;
import org.basex.core.*;
import org.basex.core.cmd.*;
import org.basex.data.*;
import org.basex.index.name.*;
import org.basex.io.*;
import org.basex.io.in.DataInput;
import org.basex.io.out.*;
import org.basex.io.out.DataOutput;
import org.basex.io.random.*;
import org.basex.util.*;
/**
* This class creates a database instance on disk.
* The storage layout is described in the {@link Data} class.
*
* @author BaseX Team 2005-17, 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;
/** Static options. */
private final StaticOptions sopts;
/** Debug counter. */
private int c;
/**
* Constructor.
* @param name name of database
* @param parser parser
* @param sopts static options
* @param opts main options
*/
public DiskBuilder(final String name, final Parser parser, final StaticOptions sopts,
final MainOptions opts) {
super(name, parser);
this.sopts = sopts;
meta = new MetaData(dbName, opts, sopts);
}
@Override
public DiskData build() throws IOException {
meta.assign(parser);
meta.dirty = true;
// calculate optimized output buffer sizes to reduce disk fragmentation
final Runtime rt = Runtime.getRuntime();
final long max = Math.min(1 << 22, rt.maxMemory() - rt.freeMemory() >> 2);
int bs = (int) Math.min(meta.filesize, max);
bs = Math.max(IO.BLOCKSIZE, bs - bs % IO.BLOCKSIZE);
// drop old database (if available) and create new one
DropDB.drop(dbName, sopts);
sopts.dbPath(dbName).md();
elemNames = new Names(meta);
attrNames = new Names(meta);
try {
try {
tout = new DataOutput(new TableOutput(meta, DATATBL));
xout = new DataOutput(meta.dbfile(DATATXT), bs);
vout = new DataOutput(meta.dbfile(DATAATV), bs);
sout = new DataOutput(meta.dbfile(DATATMP), bs);
parse();
} finally {
if(tout != null) tout.close();
if(xout != null) xout.close();
if(vout != null) vout.close();
if(sout != null) sout.close();
}
// copy temporary values into database table
try(DataInput in = new DataInput(meta.dbfile(DATATMP))) {
final TableAccess ta = new TableDiskAccess(meta, true);
try {
for(; spos < ssize; ++spos) ta.write4(in.readNum(), 8, in.readNum());
} finally {
ta.close();
}
}
meta.dbfile(DATATMP).delete();
// return database instance
return new DiskData(meta, elemNames, attrNames, path, nspaces);
} catch(final Throwable th) {
DropDB.drop(meta.name, sopts);
throw th;
}
}
@Override
public DataClip dataClip() throws IOException {
return new DataClip(build());
}
@Override
protected void addDoc(final byte[] value) throws IOException {
tout.write1(Data.DOC);
tout.write2(0);
tout.write5(textRef(value, true));
tout.write4(0);
tout.write4(meta.size++);
}
@Override
protected void addElem(final int dist, final int nameId, final int asize, final int uriId,
final boolean ne) throws IOException {
tout.write1(asize << 3 | Data.ELEM);
tout.write2((ne ? 1 << 15 : 0) | nameId);
tout.write1(uriId);
tout.write4(dist);
tout.write4(asize);
tout.write4(meta.size++);
if(Prop.debug && (c++ & 0x7FFFF) == 0) Util.err(".");
}
@Override
protected void addAttr(final int nameId, final byte[] value, final int dist, final int uriId)
throws IOException {
tout.write1(dist << 3 | Data.ATTR);
tout.write2(nameId);
tout.write5(textRef(value, false));
tout.write4(uriId);
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(textRef(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 textRef(final byte[] value, final boolean text) throws IOException {
// inline integer value
final long v = Token.toSimpleInt(value);
if(v != Integer.MIN_VALUE) return v | IO.OFFNUM;
// store text to heap file
final DataOutput store = text ? xout : vout;
final long off = store.size();
final byte[] val = Compress.pack(value);
store.writeToken(val);
return val == value ? off : off | IO.OFFCOMP;
}
}