1 /*
   2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 
  26 package jdk.tools.jaotc.binformat;
  27 
  28 import jdk.tools.jaotc.binformat.Symbol.Binding;
  29 import jdk.tools.jaotc.binformat.Symbol.Kind;
  30 
  31 import java.io.ByteArrayOutputStream;
  32 import java.nio.ByteBuffer;
  33 import java.nio.ByteOrder;
  34 import java.util.Arrays;
  35 
  36 /**
  37  * Base class that represents content of all sections with byte-level granularity. The ByteContainer
  38  * class is backed by a ByteArrayOutputStream. This class supports writing all desired byte content
  39  * to the container using the method {@code appendBytes} and accessing the byte array using the
  40  * method {@code getByteArray}.
  41  *
  42  * The method {@code putIntAt} updates the content of {@code contentBytes}. Changes are not
  43  * reflected in {@code contentStream}.
  44  */
  45 public class ByteContainer implements Container {
  46     /**
  47      * {@code ByteBuffer} representation of {@code BinaryContainer}.
  48      */
  49     private ByteBuffer contentBytes;
  50 
  51     /**
  52      * {@code ByteArrayoutputStream} to which all appends are done.
  53      */
  54     private ByteArrayOutputStream contentStream;
  55 
  56     /**
  57      * Boolean to indicate if contentBytes was modified.
  58      */
  59     private boolean bufferModified;
  60 
  61     /**
  62      * Boolean to indicate if this section contains any relocations.
  63      */
  64     private boolean hasRelocations;
  65 
  66     /**
  67      * Name of this container, used as section name.
  68      */
  69     private String containerName;
  70     private final SymbolTable symbolTable;
  71 
  72     /**
  73      * Contains a unique id.
  74      */
  75     private int sectionId = -1;
  76 
  77     /**
  78      * Construct a {@code ByteContainer} object.
  79      */
  80     public ByteContainer(String containerName, SymbolTable symbolTable) {
  81         this.containerName = containerName;
  82         this.symbolTable = symbolTable;
  83         this.contentBytes = null;
  84         this.bufferModified = false;
  85         this.hasRelocations = false;
  86         this.contentStream = new ByteArrayOutputStream();
  87     }
  88 
  89     /**
  90      * Update byte buffer to reflect the current contents of byte stream.
  91      *
  92      * @throws InternalError throws {@code InternalError} if buffer byte array was modified
  93      */
  94     private void updateByteBuffer() {
  95         if (!bufferModified) {
  96             contentBytes = ByteBuffer.wrap(contentStream.toByteArray());
  97             // Default byte order of ByteBuffer is BIG_ENDIAN.
  98             // Set it appropriately
  99             this.contentBytes.order(ByteOrder.nativeOrder());
 100         } else {
 101             throw new InternalError("Backing byte buffer no longer in sync with byte stream");
 102         }
 103     }
 104 
 105     /**
 106      * Get the byte array of {@code ByteContainer}.
 107      *
 108      * @return byte array
 109      * @throws InternalError throws {@code InternalError} if buffer byte array was modified
 110      */
 111     public byte[] getByteArray() {
 112         if (!bufferModified) {
 113             updateByteBuffer();
 114         }
 115         return contentBytes.array();
 116     }
 117 
 118     /**
 119      * Append to byte stream. It is an error to append to stream if the byte buffer version is
 120      * changed.
 121      *
 122      * @param newBytes new content
 123      * @param off offset start offset in {@code newBytes}
 124      * @param len length of data to write
 125      * @throws InternalError throws {@code InternalError} if buffer byte array was modified
 126      */
 127     public ByteContainer appendBytes(byte[] newBytes, int off, int len) {
 128         if (bufferModified) {
 129             throw new InternalError("Backing byte buffer no longer in sync with byte stream");
 130         }
 131         contentStream.write(newBytes, off, len);
 132         return this;
 133     }
 134 
 135     public ByteContainer appendBytes(byte[] newBytes) {
 136         appendBytes(newBytes, 0, newBytes.length);
 137         return this;
 138     }
 139 
 140     public ByteContainer appendInt(int i) {
 141         if (bufferModified) {
 142             throw new InternalError("Backing byte buffer no longer in sync with byte stream");
 143         }
 144         ByteBuffer b = ByteBuffer.allocate(Integer.BYTES);
 145         b.order(ByteOrder.nativeOrder());
 146         b.putInt(i);
 147         byte[] result = b.array();
 148         contentStream.write(result, 0, result.length);
 149         return this;
 150     }
 151 
 152     public ByteContainer appendInts(int[] newInts) {
 153         if (bufferModified) {
 154             throw new InternalError("Backing byte buffer no longer in sync with byte stream");
 155         }
 156         ByteBuffer b = ByteBuffer.allocate(Integer.BYTES * newInts.length).order(ByteOrder.nativeOrder());
 157         Arrays.stream(newInts).forEach(i -> b.putInt(i));
 158         byte[] result = b.array();
 159         contentStream.write(result, 0, result.length);
 160         return this;
 161     }
 162 
 163     public void appendLong(long l) {
 164         if (bufferModified) {
 165             throw new InternalError("Backing byte buffer no longer in sync with byte stream");
 166         }
 167         ByteBuffer b = ByteBuffer.allocate(8);
 168         b.order(ByteOrder.nativeOrder());
 169         b.putLong(l);
 170         byte[] result = b.array();
 171         contentStream.write(result, 0, result.length);
 172     }
 173 
 174     /**
 175      * Return the current size of byte stream backing the BinaryContainer.
 176      *
 177      * @return size of buffer stream
 178      */
 179     public int getByteStreamSize() {
 180         return contentStream.size();
 181     }
 182 
 183     /**
 184      * Return the name of this container.
 185      *
 186      * @return string containing name
 187      */
 188     @Override
 189     public String getContainerName() {
 190         return containerName;
 191     }
 192 
 193     /**
 194      * Modify the byte buffer version of the byte output stream. Note that after calling this method
 195      * all further updates to BinaryContainer will be out of sync with byte buffer content.
 196      *
 197      * @param index index of byte to be changed
 198      * @param value new value
 199      */
 200     public void putIntAt(int index, int value) {
 201         if (!bufferModified) {
 202             updateByteBuffer();
 203         }
 204         contentBytes.putInt(index, value);
 205         bufferModified = true;
 206     }
 207 
 208     public void putLongAt(int index, long value) {
 209         if (!bufferModified) {
 210             updateByteBuffer();
 211         }
 212         contentBytes.putLong(index, value);
 213         bufferModified = true;
 214     }
 215 
 216     public void setSectionId(int id) {
 217         if (sectionId != -1) {
 218             throw new InternalError("Assigning new sectionId (old: " + sectionId + ", new: " + id + ")");
 219         }
 220         sectionId = id;
 221     }
 222 
 223     @Override
 224     public int getSectionId() {
 225         if (sectionId == -1) {
 226             throw new InternalError("Using sectionId before assigned");
 227         }
 228         return sectionId;
 229     }
 230 
 231     public Symbol createSymbol(int offset, Kind kind, Binding binding, int size, String name) {
 232         Symbol symbol = new Symbol(offset, kind, binding, this, size, name);
 233         symbolTable.addSymbol(symbol);
 234         return symbol;
 235     }
 236 
 237     public GotSymbol createGotSymbol(String name) {
 238         GotSymbol symbol = new GotSymbol(Kind.OBJECT, Binding.LOCAL, this, name);
 239         symbolTable.addSymbol(symbol);
 240         return symbol;
 241     }
 242 
 243     public GotSymbol createGotSymbol(int offset, String name) {
 244         GotSymbol symbol = new GotSymbol(offset, Kind.OBJECT, Binding.LOCAL, this, name);
 245         symbolTable.addSymbol(symbol);
 246         return symbol;
 247     }
 248 
 249     public void clear() {
 250         this.contentBytes = null;
 251         this.contentStream = null;
 252     }
 253 
 254     public void setHasRelocations() {
 255         this.hasRelocations = true;
 256     }
 257 
 258     public boolean hasRelocations() {
 259         return this.hasRelocations;
 260     }
 261 }