/* --------------------------------------------------------- *
* __________ D E L T A S C R I P T *
* (_________() *
* / === / - A fast, dynamic scripting language *
* | == | - Version 4.13.11.0 *
* / === / - Developed by Adam R. Nelson *
* | = = | - 2011-2013 *
* / === / - Distributed under GNU LGPL v3 *
* (________() - http://github.com/ar-nelson/deltascript *
* *
* --------------------------------------------------------- */
package com.sector91.delta.script.parser;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import com.sector91.delta.script.DScriptContext;
import com.sector91.delta.script.DeltaScript;
import com.sector91.delta.script.instrs.DSInstr;
/**
* <p>Writes compiled DeltaScript scripts in binary format to {@link
* OutputStream}s. DeltaScript's binary format is used in {@code .dsc} files,
* which are smaller and faster to load than {@code .ds} source files.</p>
*
* @author Adam R. Nelson
* @version 4.13.11.0
* @since 4.13.2.0
*/
public class DScriptBinaryWriter implements Closeable
{
/** The charset of string values in compiled DeltaScript files. */
public static final Charset CHARSET = Charset.forName("UTF-8");
/**
* The header that starts every compiled DeltaScript file: the 3-byte number
* {@code 0xACE91}.
*/
public static final byte[] FILE_HEADER = {0xA, (byte)0xCE, (byte)0x91};
/**
* The byte order of multibyte numbers stored in compiled DeltaScript
* files.
*/
public static final ByteOrder ORDER = ByteOrder.BIG_ENDIAN;
static final long
FLAG_DEBUG = 1L;
private final OutputStream out;
public DScriptBinaryWriter(OutputStream os) throws IOException
{out = os;}
private void writeShort(short s) throws IOException
{
final byte[] bytes = ByteBuffer.allocate(2).order(ORDER).putShort(s)
.array();
out.write(bytes);
}
private void writeInt(int i) throws IOException
{
final byte[] bytes = ByteBuffer.allocate(4).order(ORDER).putInt(i)
.array();
out.write(bytes);
}
private void writeLong(long l) throws IOException
{
final byte[] bytes = ByteBuffer.allocate(8).order(ORDER).putLong(l)
.array();
out.write(bytes);
}
/**
* <p>Writes a script in binary format to this writer's output stream. The
* stream will be flushed, but not closed, after this operation is
* complete.</p>
*
* @param script The compiled script to write to the stream.
* @param context Used to determine some binary-format-related settings,
* such as whether to retain line numbers in the output.
* @throws IOException If any error occurs while writing the data.
*/
public void write(DSInstr script, DScriptContext context)
throws IOException
{
// TODO: Determine whether to write debug info based on the context.
final boolean debug = true;
long flags = 0;
if (debug)
flags |= FLAG_DEBUG;
writeHeader(flags);
writeInstr(script, debug);
out.flush();
}
private void writeHeader(long flags)
throws IOException
{
out.write(FILE_HEADER); // 0xACE91, in 3 bytes.
out.write(DeltaScript.VERSION.majorVersion());
writeLong(flags);
}
private void writeInstr(DSInstr instr, boolean debug)
throws IOException
{
out.write(instr.type().opcode);
if (instr.head() == null)
writeInt(-1);
else
{
final byte[] strBytes = instr.head().getBytes(CHARSET);
writeInt(strBytes.length);
out.write(strBytes);
}
if (debug)
{
writeInt(instr.sourceStart());
writeShort(instr.sourceLength());
}
writeInt(instr.tail().length);
for (DSInstr subinstr : instr.tail())
writeInstr(subinstr, debug);
}
public void close() throws IOException
{out.close();}
}