floppppp 🐉💾
This commit is contained in:
commit
6600d02c48
|
@ -0,0 +1,4 @@
|
|||
*.byte
|
||||
*.class
|
||||
*.cmi
|
||||
*.cmo
|
|
@ -0,0 +1,299 @@
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Jocaml {
|
||||
private static String TRAILER_MAGIC = "Caml1999X028";
|
||||
private static long MARSHAL_MAGIC_SMALL = 0x8495A6BE;
|
||||
private static long MARSHAL_MAGIC_BIG = 0x8495A6BF;
|
||||
|
||||
private static class CodeOffset {
|
||||
public final int offset;
|
||||
|
||||
public CodeOffset(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "CodeOffset<" + offset + ">";
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof CodeOffset && ((CodeOffset) other).offset == this.offset;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return offset * 13;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Block {
|
||||
public final int tag;
|
||||
public final List<Object> contents;
|
||||
|
||||
public Block(int tag, List<Object> contents) {
|
||||
this.tag = tag;
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Block<" + tag + ": " + contents.toString() + ">";
|
||||
}
|
||||
}
|
||||
|
||||
private static class Section {
|
||||
String name;
|
||||
ByteBuffer data;
|
||||
|
||||
public Section(String name, ByteBuffer data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Section<name=" + name + ", length=" + data.capacity() + ">";
|
||||
}
|
||||
|
||||
public List<String> unstring() {
|
||||
List<String> strings = new ArrayList<>();
|
||||
for (int i = 0; i < this.data.capacity();) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
while (this.data.get(i) != 0) {
|
||||
buf.append((char) this.data.get(i));
|
||||
i += 1;
|
||||
}
|
||||
strings.add(buf.toString());
|
||||
i += 1;
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
private Object parseNextObject(List<Object> shared) throws Exception {
|
||||
int nextTag = this.data.get() & (short) 0xff;
|
||||
switch (nextTag) {
|
||||
// integers
|
||||
case 0x00: {
|
||||
byte value = this.data.get();
|
||||
return Long.valueOf(value);
|
||||
}
|
||||
case 0x01: {
|
||||
short value = this.data.getShort();
|
||||
return Long.valueOf(value);
|
||||
}
|
||||
case 0x02: {
|
||||
int value = this.data.getInt();
|
||||
return Long.valueOf(value);
|
||||
}
|
||||
case 0x03: {
|
||||
long value = this.data.getLong();
|
||||
return Long.valueOf(value);
|
||||
}
|
||||
|
||||
// shared elements
|
||||
case 0x04: {
|
||||
long offset = this.data.get() & (long) 0xff;
|
||||
return shared.get((int) offset);
|
||||
}
|
||||
case 0x05: {
|
||||
long offset = this.data.getShort() & (long) 0xff;
|
||||
return shared.get((int) offset);
|
||||
}
|
||||
case 0x06: {
|
||||
long offset = this.data.getInt() & (long) 0xff;
|
||||
return shared.get((int) offset);
|
||||
}
|
||||
|
||||
// blocks
|
||||
case 0x08: {
|
||||
long header = this.data.getInt() & (long) 0xff;
|
||||
int tag = (int) header & 0xff;
|
||||
long size = header >> 10;
|
||||
List<Object> elems = new ArrayList<>();
|
||||
Block block = new Block(tag, elems);
|
||||
if (size > 0) {
|
||||
shared.add(block);
|
||||
for (int i = 0; i < size; i++) {
|
||||
elems.add(parseNextObject(shared));
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
case 0x13: {
|
||||
throw new Exception("long block not supported");
|
||||
}
|
||||
|
||||
// string
|
||||
case 0x09: {
|
||||
int len = this.data.get() & (short) 0xff;
|
||||
byte[] buf = new byte[len];
|
||||
this.data.get(buf);
|
||||
String s = new String(buf);
|
||||
shared.add(s);
|
||||
return s;
|
||||
}
|
||||
case 0x0a: {
|
||||
long len = this.data.getInt() & (long) 0xff;
|
||||
byte[] buf = new byte[(int)len];
|
||||
this.data.get(buf);
|
||||
String s = new String(buf);
|
||||
shared.add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// float
|
||||
case 0x0b:
|
||||
case 0x0c: {
|
||||
Double obj = Double.valueOf(this.data.getDouble());
|
||||
shared.add(obj);
|
||||
return obj;
|
||||
}
|
||||
case 0x0D:
|
||||
case 0x0E: {
|
||||
throw new Exception("double array not supported");
|
||||
}
|
||||
case 0x07:
|
||||
case 0x0f: {
|
||||
throw new Exception("double array not supported");
|
||||
}
|
||||
|
||||
// misc
|
||||
case 0x10: {
|
||||
throw new Exception("code pointer with checksum");
|
||||
}
|
||||
case 0x11: {
|
||||
throw new Exception("code pointer with closure");
|
||||
}
|
||||
case 0x12:
|
||||
case 0x18:
|
||||
case 0x19: {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
while (true) {
|
||||
int next = this.data.get() & (short) 0xff;
|
||||
if (next == 0) {
|
||||
break;
|
||||
}
|
||||
buf.append((char) next);
|
||||
}
|
||||
String name = buf.toString();
|
||||
switch (name) {
|
||||
// int64
|
||||
case "_j": {
|
||||
long v = Long.valueOf(this.data.getLong());
|
||||
shared.add(v);
|
||||
return v;
|
||||
}
|
||||
default:
|
||||
throw new Exception("unknown custom op: " + name);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
if (nextTag < 0x20) {
|
||||
throw new Exception("invalid tag " + nextTag);
|
||||
} else if (nextTag < 0x40) {
|
||||
int length = nextTag & 0x1f;
|
||||
byte[] buf = new byte[(int)length];
|
||||
this.data.get(buf);
|
||||
String s = new String(buf);
|
||||
shared.add(s);
|
||||
return s;
|
||||
} else if (nextTag < 0x80) {
|
||||
Long value = Long.valueOf(nextTag & 0x3f);
|
||||
return value;
|
||||
} else {
|
||||
long size = (nextTag >> 4) & 0x07;
|
||||
int tag = nextTag & 0x0f;
|
||||
List<Object> elems = new ArrayList<>();
|
||||
Block block = new Block(tag, elems);
|
||||
if (size > 0) {
|
||||
shared.add(block);
|
||||
for (int i = 0; i < size; i++) {
|
||||
elems.add(parseNextObject(shared));
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unmarshal() throws Exception {
|
||||
int magic = this.data.getInt(0);
|
||||
if (magic != MARSHAL_MAGIC_SMALL) {
|
||||
throw new Exception("bad marshal magic");
|
||||
}
|
||||
|
||||
int byteLength = this.data.getInt(4);
|
||||
int numObjs = this.data.getInt(8);
|
||||
System.out.println(numObjs);
|
||||
|
||||
List<Object> shared = new ArrayList<>();
|
||||
List<Object> objs = new ArrayList<>();
|
||||
|
||||
this.data.position(20);
|
||||
|
||||
while (this.data.position() < this.data.capacity()) {
|
||||
Object next = parseNextObject(shared);
|
||||
objs.add(next);
|
||||
}
|
||||
|
||||
System.out.println(shared.size());
|
||||
|
||||
System.out.println(objs);
|
||||
}
|
||||
}
|
||||
|
||||
public static Section findSection(Section[] sections, String name) {
|
||||
for (Section section : sections) {
|
||||
if (section.name.equals(name)) {
|
||||
return section;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ByteBuffer buf;
|
||||
int len;
|
||||
{
|
||||
FileInputStream in = new FileInputStream(new File(args[0]));
|
||||
len = in.available();
|
||||
buf = ByteBuffer.allocate(len);
|
||||
Channels.newChannel(in).read(buf);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (buf.get(len - 12 + i) != (byte) TRAILER_MAGIC.charAt(i)) {
|
||||
throw new Exception("invalid trailer");
|
||||
}
|
||||
}
|
||||
|
||||
int numSections = buf.getInt(len - 16);
|
||||
Section[] sections = new Section[numSections];
|
||||
|
||||
for (int i = numSections - 1, sectionBase = len - 16 - (numSections * 8); i >= 0; i--) {
|
||||
int base = len - 16 - (numSections * 8) + (i * 8);
|
||||
char[] name = new char[4];
|
||||
name[0] = (char) buf.get(base + 0);
|
||||
name[1] = (char) buf.get(base + 1);
|
||||
name[2] = (char) buf.get(base + 2);
|
||||
name[3] = (char) buf.get(base + 3);
|
||||
int sectionLength = buf.getInt(base + 4);
|
||||
sectionBase -= sectionLength;
|
||||
byte[] data = new byte[sectionLength];
|
||||
buf.position(sectionBase);
|
||||
buf.get(data);
|
||||
sections[i] = new Section(new String(name), ByteBuffer.wrap(data));
|
||||
}
|
||||
|
||||
System.out.println(Arrays.toString(sections));
|
||||
findSection(sections, "SYMB").unmarshal();
|
||||
findSection(sections, "CRCS").unmarshal();
|
||||
findSection(sections, "DATA").unmarshal();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
.PHONY: all run clean
|
||||
|
||||
all: Jocaml.class
|
||||
|
||||
run: Jocaml.class test.byte
|
||||
java Jocaml test.byte
|
||||
|
||||
clean:
|
||||
$(RM) *.class *.cmi *.byte *.cmo
|
||||
|
||||
test.byte: test.ml
|
||||
ocamlfind ocamlc -linkpkg -o test.byte test.ml
|
||||
|
||||
%.class: %.java
|
||||
javac $<
|
Loading…
Reference in New Issue