package org.jruby.ir.persistence;
import org.jcodings.Encoding;
import org.jruby.RubySymbol;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRScopeType;
import org.jruby.ir.Operation;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.OperandType;
import org.jruby.parser.StaticScope;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.jruby.runtime.RubyEvent;
import org.jruby.runtime.Signature;
import org.jruby.util.ByteList;
import static com.headius.backport9.buffer.Buffers.flipBuffer;
public class IRWriterStream implements IRWriterEncoder, IRPersistenceValues {
private final Map<IRScope, Integer> scopeInstructionOffsets = new HashMap<>();
private final ByteBuffer buf = ByteBuffer.allocate(TWO_MEGS);
private final OutputStream stream;
private final IRWriterAnalyzer analyzer;
int = -1;
int poolOffset = -1;
public IRWriterStream(OutputStream stream) {
this.stream = stream;
this.analyzer = new IRWriterAnalyzer();
}
public IRWriterStream(File file) throws FileNotFoundException {
this(new FileOutputStream(file));
}
public void addScopeInstructionOffset(IRScope scope) {
scopeInstructionOffsets.put(scope, offset());
}
private int offset() {
return buf.position() + PROLOGUE_LENGTH;
}
public int getScopeInstructionOffset(IRScope scope) {
return scopeInstructionOffsets.get(scope);
}
@Override
public void encode(boolean value) {
buf.put((byte) (value ? TRUE : FALSE));
}
@Override
public void encode(byte value) {
buf.put(value);
}
@Override
public void encode(char value) {
buf.putChar(value);
}
@Override
public void encode(int value) {
if (value >= 0 && value <= 127) {
buf.put((byte) value);
} else {
buf.put(FULL);
buf.putInt(value);
}
}
@Override
public void encode(int[] value) {
encode(value.length);
for (int i = 0; i < value.length; i++) {
encode(value[i]);
}
}
@Override
public void encode(long value) {
if (value >= 0 && value <= 127) {
encode((byte) value);
} else {
buf.put(FULL);
buf.putLong(value);
}
}
@Override
public void encode(float value) {
buf.putFloat(value);
}
@Override
public void encode(double value) {
buf.putDouble(value);
}
@Override
public void encode(ByteList value) {
encode(value.bytes());
encode(value.getEncoding());
}
@Override
public void encode(byte[] bytes) {
encode(bytes.length);
buf.put(bytes);
}
@Override
public void encode(Encoding encoding) {
encode(encoding.getName());
}
@Override
public void encode(RubySymbol symbol) {
if (symbol == null) {
encode(NULL_STRING);
} else {
encode(symbol.getBytes());
}
}
@Override
public void encode(String value) {
if (value == null) {
encode(NULL_STRING);
} else {
byte[] bytes = value.getBytes();
encode(bytes.length);
buf.put(bytes);
}
}
@Override
public void encode(String[] values) {
if (values == null) {
encode((int) 0);
return;
}
encode(values.length);
for (String value : values) {
encode(value.length());
buf.put(value.getBytes());
}
}
@Override
public void encode(Operand operand) {
operand.encode(this);
}
@Override
public void encode(Operand[] operands) {
encode(operands.length);
for(Operand arg: operands) {
encode(arg);
}
}
@Override
public void encode(Instr instr) {
instr.encode(this);
}
@Override
public void encode(IRScope value) {
encode(analyzer.getScopeID(value));
}
@Override
public void encode(IRScopeType value) {
encode((byte) value.ordinal());
}
@Override
public void encode(Signature signature) {
if (signature == null) signature = Signature.NO_ARGUMENTS;
encode(signature.encode());
}
@Override
public void encode(RubyEvent event) {
encode((byte) event.ordinal());
}
@Override
public void encode(StaticScope.Type value) {
encode((byte) value.ordinal());
}
@Override
public void encode(Operation value) {
encode(value.ordinal());
}
@Override
public void encode(OperandType value) {
encode((byte) value.ordinal());
}
@Override
public void (IRScope scope) {
}
@Override
public void (IRScope scope) {
encode(getScopeInstructionOffset(scope));
}
@Override
public void startEncodingScopeInstrs(IRScope scope) {
addScopeInstructionOffset(scope);
encode(scope.getInterpreterContext().getInstructions().length);
}
@Override
public void endEncodingScopeInstrs(IRScope scope) {
}
@Override
public void (IRScope script) {
headersOffset = offset();
encode(analyzer.getScopeCount());
}
@Override
public void (IRScope script) {
}
@Override
public void startEncoding(IRScope script) {
try {
IRWriter.persist(analyzer, script);
} catch (IOException ex) {
}
}
@Override
public void endEncoding(IRScope script) {
try {
stream.write(ByteBuffer.allocate(4).putInt(VERSION).array());
stream.write(ByteBuffer.allocate(4).putInt(headersOffset).array());
stream.write(ByteBuffer.allocate(4).putInt(poolOffset).array());
flipBuffer(buf);
stream.write(buf.array(), buf.position(), buf.limit());
stream.close();
} catch (IOException e) {
try { if (stream != null) stream.close(); } catch (IOException e1) {}
}
}
}