/*
 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.oracle.objectfile.io;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;

This is a wrapper for ByteBuffer which provides assembler-style operations to read and write binary data at the granularity of bytes, words etc.. Its interface is intentionally modeled on the analogous directives in the Oracle Solaris x86 assembler, as(1). "http://docs.oracle.com/cd/E26502_01/html/E28388/". Since this class supports both writing (assembling) and reading (disassembling), each assembler directive has a corresponding "read" and "write" method in this class. A note about signedness: for writing, there is no need for signed/unsigned versions of each directive. Instead, it's the caller's responsibility to pass a (signed) value whose two's complement encoding is the desired bit pattern (e.g. -1 for the 32-bit value 0xffffffff). When reading, this is more complicated: if we read a byte with value 0xff, should we return (short) 255, or (byte) -1? The latter is more consistent with the writing behavior, but also more confusing when considered in isolation. So far, this distinction has only been important when reading bytes, and we provide explicit readUbyte() and readByte() methods to cater to each requirement separately. FIXME: this should probably be expanded to cover the full range of integer data types (except long, which we're stuck with).
/** * This is a wrapper for ByteBuffer which provides assembler-style operations to read and write * binary data at the granularity of bytes, words etc.. Its interface is intentionally modeled on * the analogous directives in the Oracle Solaris x86 assembler, as(1). * {@link "http://docs.oracle.com/cd/E26502_01/html/E28388/"}. Since this class supports both * writing (assembling) and reading (disassembling), each assembler directive has a corresponding * "read" and "write" method in this class. * * A note about signedness: for writing, there is no need for signed/unsigned versions of each * directive. Instead, it's the caller's responsibility to pass a (signed) value whose two's * complement encoding is the desired bit pattern (e.g. -1 for the 32-bit value 0xffffffff). When * reading, this is more complicated: if we read a byte with value 0xff, should we return (short) * 255, or (byte) -1? The latter is more consistent with the writing behavior, but also more * confusing when considered in isolation. So far, this distinction has only been important when * reading bytes, and we provide explicit readUbyte() and readByte() methods to cater to each * requirement separately. FIXME: this should probably be expanded to cover the full range of * integer data types (except long, which we're stuck with). */
public class AssemblyBuffer implements InputDisassembler, OutputAssembler { private static final double GROWTH_FACTOR = 2; public static final int INITIAL_STRING_SIZE = 64; public static InputDisassembler createInputDisassembler(ByteBuffer in) { return new AssemblyBuffer(in); } public static OutputAssembler createOutputAssembler(ByteBuffer out) { return new AssemblyBuffer(out); } public static OutputAssembler createOutputAssembler(ByteOrder order) { return new AssemblyBuffer(order); } public static OutputAssembler createOutputAssembler() { return new AssemblyBuffer(); } ByteBuffer buf; public AssemblyBuffer(ByteBuffer in) { this.buf = in; } public AssemblyBuffer(ByteOrder order) { this(ByteBuffer.allocate(16).order(order)); } public AssemblyBuffer() { this(ByteOrder.nativeOrder()); } @Override public boolean eof() { return !buf.hasRemaining(); }
See org.graalvm.compiler.serviceprovider.BufferUtil.
/** * See {@code org.graalvm.compiler.serviceprovider.BufferUtil}. */
private static Buffer asBaseBuffer(Buffer obj) { return obj; } @Override public void seek(long pos) { if (pos > pos()) { ensure((int) (pos - pos())); } asBaseBuffer(buf).position((int) pos); } @Override public void skip(long diff) { seek(pos() + diff); } private Deque<Long> seekStack = new ArrayDeque<>(); @Override public void pushSeek(long pos) { seekStack.push((long) pos()); seek(pos); } @Override public void pushSkip(long diff) { seekStack.push((long) pos()); seek(pos() + diff); } @Override public void pushPos() { seekStack.push((long) pos()); } @Override public void pop() { seek(seekStack.pop()); } @Override public int pos() { return buf.position(); } @Override public void align(int alignment) { while (pos() % alignment != 0) { ensure(1); skip(1); // put((byte) 0); } } @Override public void even() { align(2); } @Override public void writeZero(int n) { for (int i = 0; i < n; ++i) { writeByte((byte) 0); } } @Override public void writeLEB128(long v) { long vv = v; if (vv == 0L) { writeByte((byte) 0); return; } do { byte b = (byte) (vv & 0x7F); vv >>= 7; if (vv != 0L) { b |= (byte) 0x80; } writeByte(b); } while (vv != 0L); } @Override public void writeSLEB128(long v) { long vv = v; boolean more = true; while (more) { byte b = (byte) (vv & 0x7f); vv >>= 7; // arithmetic shift! // sign bit of b is second high order bit (0x40) if ((vv == 0L && (b & 0x40) == 0) || (vv == -1L && (b & 0x40) == 0x40)) { more = false; } else { b |= 0x80; } writeByte(b); } } @Override public void writeBCD(double f) { throw new UnsupportedOperationException(); } @Override public void writeFloat(float f) { throw new UnsupportedOperationException(); } @Override public void writeDouble(double f) { throw new UnsupportedOperationException(); } @Override public void writeByte(byte b) { ensure(1); buf.put(b); } @Override public void write4Byte(int w) { ensure(4); buf.putInt(w); } @Override public void writeBlob(byte[] blob) { ensure(blob.length); buf.put(blob); } @Override public void write2Byte(short i) { ensure(2); buf.putShort(i); } @Override public void writeValue(short i) { write2Byte(i); } @Override public void write8Byte(long w) { ensure(8); buf.putLong(w); } @Override public void writeQuad(long w) { write8Byte(w); } @Override public void writeString(String s) { if (s != null) { writeBlob(s.getBytes()); } writeByte((byte) 0); } @Override public void writeStringPadded(String s, int nBytes) { ensure(nBytes); assert s == null || s.length() < nBytes : "string oversize: " + s; // < b.c. of trailing \0 writeString(s); final int pad = nBytes - (s == null ? 0 : s.length()) - 1; for (int i = 0; i < pad; ++i) { writeByte((byte) 0); } } @Override public ByteBuffer getBuffer() { return buf; }
Ensure there is enough space left in the buffer to write the given number of bytes.
/** * Ensure there is enough space left in the buffer to write the given number of bytes. */
private void ensure(int n) { if (buf.remaining() < n) { // determine a large enough grow factor final int cap = buf.capacity(); final int pos = buf.position(); final int req = pos + n; int newCap = (int) (cap * GROWTH_FACTOR); while (newCap < req) { newCap = (int) (newCap * GROWTH_FACTOR); } // grow and replace ByteBuffer nbuf = ByteBuffer.allocate(newCap); nbuf.order(ByteOrder.nativeOrder()); byte[] old = new byte[pos]; asBaseBuffer(buf).rewind(); buf.get(old); nbuf.put(old); buf = nbuf; } } @Override public byte[] getBlob() { int len = buf.position(); byte[] bytes = new byte[len]; asBaseBuffer(buf).position(0); buf.get(bytes); return bytes; } @Override public void setByteOrder(ByteOrder byteOrder) { buf.order(byteOrder); } @Override public void skip(int n) { ensure(n); seek(pos() + n); } @Override public ByteOrder getByteOrder() { return buf.order(); } @Override public void alignRead(int alignment) { while (pos() % alignment != 0) { buf.get(); } } @Override public int read4Byte() { return buf.getInt(); } @Override public byte readByte() { return buf.get(); } @Override public char readUbyte() { return (char) (buf.get() & 0xff); } @Override public short read2Byte() { return buf.getShort(); } @Override public short readValue() { return read2Byte(); } @Override public long read8Byte() { return buf.getLong(); } @Override public long readQuad() { return read8Byte(); } @Override public void writeTruncatedLong(long value, int truncateTo) { switch (truncateTo) { case 8: write8Byte(value); break; case 4: write4Byte((int) value); break; case 2: write2Byte((short) value); break; case 1: writeByte((byte) value); break; default: throw new IllegalArgumentException("can only truncate to powers-of-two <= 8"); } } @Override public long readTruncatedLong(int truncateTo) { switch (truncateTo) { case 8: return read8Byte(); case 4: return read4Byte(); case 2: return read2Byte(); case 1: return readUbyte(); default: throw new IllegalArgumentException("can only truncate to powers-of-two <= 8"); } } @Override public String readZeroTerminatedString() { char[] buffer = new char[INITIAL_STRING_SIZE]; int w = 1; buffer[0] = (char) buf.get(); if (buffer[0] == '\0') { return ""; } do { if (w == buffer.length) { buffer = Arrays.copyOf(buffer, 2 * buffer.length); } buffer[w] = (char) buf.get(); } while (buffer[w++] != '\0'); return String.valueOf(buffer, 0, w - 1); } @Override public long readLEB128() { long b = buf.get(); if (b == 0L) { return 0L; } long r = 0L; int shift = 0; while ((b & 0x80) != 0) { r |= (b & 0x7f) << shift; shift += 7; b = buf.get(); } r |= b << shift; return r; } @Override public long readSLEB128() { long r = 0L; int shift = 0; byte b; while (true) { b = buf.get(); r |= (b & 0x7f) << shift; shift += 7; if ((b & 0x80) == 0) { break; } } // sign bit of b is second high order bit (0x40) // 64 is the number of bits in a long if ((shift < 64) && (b & 0x40) == 0x40) { r |= -(1 << shift); } return r; } @Override public byte[] readBlob(int length) { byte[] blob = new byte[length]; buf.get(blob); return blob; } }