blob: 3d44ab0cf72dd6062afe244eef6bd24592c4d674 [file] [log] [blame]
/*
* Copyright (c) 2016, 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.
*
* 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 jdk.tools.jaotc.binformat;
import jdk.tools.jaotc.binformat.Symbol.Binding;
import jdk.tools.jaotc.binformat.Symbol.Kind;
import jdk.tools.jaotc.binformat.Container;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
/**
* Base class that represents content of all sections with byte-level granularity. The ByteContainer
* class is backed by a ByteArrayOutputStream. This class supports writing all desired byte content
* to the container using the method {@code appendBytes} and accessing the byte array using the
* method {@code getByteArray}.
*
* The method {@code putIntAt} updates the content of {@code contentBytes}. Changes are not
* reflected in {@code contentStream}.
*/
public class ByteContainer implements Container {
/**
* {@code ByteBuffer} representation of {@code BinaryContainer}.
*/
private ByteBuffer contentBytes;
/**
* {@code ByteArrayoutputStream} to which all appends are done.
*/
private ByteArrayOutputStream contentStream;
/**
* Boolean to indicate if contentBytes was modified.
*/
private boolean bufferModified;
/**
* Boolean to indicate if this section contains any relocations.
*/
private boolean hasRelocations;
/**
* Name of this container, used as section name.
*/
private String containerName;
private final SymbolTable symbolTable;
/**
* Contains a unique id.
*/
private int sectionId = -1;
/**
* Construct a {@code ByteContainer} object.
*/
public ByteContainer(String containerName, SymbolTable symbolTable) {
this.containerName = containerName;
this.symbolTable = symbolTable;
this.contentBytes = null;
this.bufferModified = false;
this.hasRelocations = false;
this.contentStream = new ByteArrayOutputStream();
}
/**
* Update byte buffer to reflect the current contents of byte stream.
*
* @throws InternalError throws {@code InternalError} if buffer byte array was modified
*/
private void updateByteBuffer() {
if (!bufferModified) {
contentBytes = ByteBuffer.wrap(contentStream.toByteArray());
// Default byte order of ByteBuffer is BIG_ENDIAN.
// Set it appropriately
this.contentBytes.order(ByteOrder.nativeOrder());
} else {
throw new InternalError("Backing byte buffer no longer in sync with byte stream");
}
}
/**
* Get the byte array of {@code ByteContainer}.
*
* @return byte array
* @throws InternalError throws {@code InternalError} if buffer byte array was modified
*/
public byte[] getByteArray() {
if (!bufferModified) {
updateByteBuffer();
}
return contentBytes.array();
}
/**
* Append to byte stream. It is an error to append to stream if the byte buffer version is
* changed.
*
* @param newBytes new content
* @param off offset start offset in {@code newBytes}
* @param len length of data to write
* @throws InternalError throws {@code InternalError} if buffer byte array was modified
*/
public ByteContainer appendBytes(byte[] newBytes, int off, int len) {
if (bufferModified) {
throw new InternalError("Backing byte buffer no longer in sync with byte stream");
}
contentStream.write(newBytes, off, len);
return this;
}
public ByteContainer appendBytes(byte[] newBytes) {
appendBytes(newBytes, 0, newBytes.length);
return this;
}
public ByteContainer appendInt(int i) {
if (bufferModified) {
throw new InternalError("Backing byte buffer no longer in sync with byte stream");
}
ByteBuffer b = ByteBuffer.allocate(Integer.BYTES);
b.order(ByteOrder.nativeOrder());
b.putInt(i);
byte[] result = b.array();
contentStream.write(result, 0, result.length);
return this;
}
public ByteContainer appendInts(int[] newInts) {
if (bufferModified) {
throw new InternalError("Backing byte buffer no longer in sync with byte stream");
}
ByteBuffer b = ByteBuffer.allocate(Integer.BYTES * newInts.length).order(ByteOrder.nativeOrder());
Arrays.stream(newInts).forEach(i -> b.putInt(i));
byte[] result = b.array();
contentStream.write(result, 0, result.length);
return this;
}
public void appendLong(long l) {
if (bufferModified) {
throw new InternalError("Backing byte buffer no longer in sync with byte stream");
}
ByteBuffer b = ByteBuffer.allocate(8);
b.order(ByteOrder.nativeOrder());
b.putLong(l);
byte[] result = b.array();
contentStream.write(result, 0, result.length);
}
/**
* Return the current size of byte stream backing the BinaryContainer.
*
* @return size of buffer stream
*/
public int getByteStreamSize() {
return contentStream.size();
}
/**
* Return the name of this container.
*
* @return string containing name
*/
public String getContainerName() {
return containerName;
}
/**
* Modify the byte buffer version of the byte output stream. Note that after calling this method
* all further updates to BinaryContainer will be out of sync with byte buffer content.
*
* @param index index of byte to be changed
* @param value new value
*/
public void putIntAt(int index, int value) {
if (!bufferModified) {
updateByteBuffer();
}
contentBytes.putInt(index, value);
bufferModified = true;
}
public void putLongAt(int index, long value) {
if (!bufferModified) {
updateByteBuffer();
}
contentBytes.putLong(index, value);
bufferModified = true;
}
public void setSectionId(int id) {
if (sectionId != -1) {
throw new InternalError("Assigning new sectionId (old: " + sectionId + ", new: " + id + ")");
}
sectionId = id;
}
public int getSectionId() {
if (sectionId == -1) {
throw new InternalError("Using sectionId before assigned");
}
return sectionId;
}
public Symbol createSymbol(int offset, Kind kind, Binding binding, int size, String name) {
Symbol symbol = new Symbol(offset, kind, binding, this, size, name);
symbolTable.addSymbol(symbol);
return symbol;
}
public GotSymbol createGotSymbol(String name) {
GotSymbol symbol = new GotSymbol(Kind.OBJECT, Binding.LOCAL, this, name);
symbolTable.addSymbol(symbol);
return symbol;
}
public GotSymbol createGotSymbol(int offset, String name) {
GotSymbol symbol = new GotSymbol(offset, Kind.OBJECT, Binding.LOCAL, this, name);
symbolTable.addSymbol(symbol);
return symbol;
}
public void clear() {
this.contentBytes = null;
this.contentStream = null;
}
public void setHasRelocations() {
this.hasRelocations = true;
}
public boolean hasRelocations() {
return this.hasRelocations;
}
}