/* --------------------------------------------------------- * * __________ 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();} }