All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.objectfile.io.AssemblyBuffer Maven / Gradle / Ivy

There is a newer version: 19.2.1
Show newest version
/*
 * 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.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).
 * {@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();
    }

    @Override
    public void seek(long pos) {
        if (pos > pos()) {
            ensure((int) (pos - pos()));
        }
        buf.position((int) pos);
    }

    @Override
    public void skip(long diff) {
        seek(pos() + diff);
    }

    private Deque 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.
     */
    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];
            buf.rewind();
            buf.get(old);
            nbuf.put(old);
            buf = nbuf;
        }
    }

    @Override
    public byte[] getBlob() {
        int len = buf.position();
        byte[] bytes = new byte[len];
        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;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy