package org.elixir_lang.beam.chunk; import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.DataInputStream; import java.io.IOException; import static com.intellij.openapi.util.Pair.pair; /** * Chunk of a `.beam` file. Same chunk format as base IFF */ public class Chunk { private static final int ALIGNMENT = 4; @NotNull public final String typeID; @NotNull public final byte[] data; private Chunk(@NotNull String typeID, @NotNull byte[] data) { this.typeID = typeID; this.data = data; } @Nullable public static Chunk from(@NotNull DataInputStream dataInputStream) throws IOException { String typeID = typeID(dataInputStream); Chunk chunk = null; if (typeID != null) { long length = length(dataInputStream); byte[] data = new byte[(int) length]; dataInputStream.readFully(data); int padding = (int) ((ALIGNMENT - (length % ALIGNMENT)) % ALIGNMENT); dataInputStream.skipBytes(padding); chunk = new Chunk(typeID, data); } return chunk; } public static long length(@NotNull DataInputStream dataInputStream) throws IOException { return readUnsignedInt(dataInputStream); } @Nullable public static String typeID(@NotNull DataInputStream dataInputStream) throws IOException { byte[] bytes = new byte[4]; int bytesRead = dataInputStream.read(bytes, 0, bytes.length); String typeID = null; if (bytesRead == bytes.length) { typeID = new String(bytes); } return typeID; } private static long readUnsignedInt(@NotNull DataInputStream dataInputStream) throws IOException { byte[] bytes = new byte[32 / 8]; dataInputStream.readFully(bytes); return unsignedInt(bytes).first; } @NotNull @Contract(pure = true) @SuppressWarnings("PMD.DefaultPackage") static Pair<Integer, Integer> unsignedByte(byte signedByte) { return pair(signedByte & 0xFF, 1); } @NotNull @Contract(pure = true) @SuppressWarnings("PMD.DefaultPackage") static Pair<Long, Integer> unsignedInt(@NotNull byte[] bytes) { return unsignedInt(bytes, 0); } @NotNull @Contract(pure = true) @SuppressWarnings("PMD.DefaultPackage") static Pair<Long, Integer> unsignedInt(@NotNull byte[] bytes, int offset) { assert bytes.length >= offset + 4; long unsignedInt = 0; for (int i = 0; i < 4; i++) { int unsignedByte = unsignedByte(bytes[offset + i]).first; unsignedInt += unsignedByte << 8 * (4 - 1 - i); } return pair(unsignedInt, 4); } public enum TypeID { ATOM("Atom"), EXPT("ExpT"); private final String typeID; TypeID(@NotNull String typeID) { this.typeID = typeID; } @Override public String toString() { return typeID; } } }