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 24package jdk.tools.jaotc.binformat; 25 26import jdk.tools.jaotc.binformat.Symbol.Binding; 27import jdk.tools.jaotc.binformat.Symbol.Kind; 28import jdk.tools.jaotc.binformat.Container; 29 30import java.io.ByteArrayOutputStream; 31import java.nio.ByteBuffer; 32import java.nio.ByteOrder; 33import java.util.Arrays; 34 35/** 36 * Base class that represents content of all sections with byte-level granularity. The ByteContainer 37 * class is backed by a ByteArrayOutputStream. This class supports writing all desired byte content 38 * to the container using the method {@code appendBytes} and accessing the byte array using the 39 * method {@code getByteArray}. 40 * 41 * The method {@code putIntAt} updates the content of {@code contentBytes}. Changes are not 42 * reflected in {@code contentStream}. 43 */ 44public class ByteContainer implements Container { 45 /** 46 * {@code ByteBuffer} representation of {@code BinaryContainer}. 47 */ 48 private ByteBuffer contentBytes; 49 50 /** 51 * {@code ByteArrayoutputStream} to which all appends are done. 52 */ 53 private ByteArrayOutputStream contentStream; 54 55 /** 56 * Boolean to indicate if contentBytes was modified. 57 */ 58 private boolean bufferModified; 59 60 /** 61 * Boolean to indicate if this section contains any relocations. 62 */ 63 private boolean hasRelocations; 64 65 /** 66 * Name of this container, used as section name. 67 */ 68 private String containerName; 69 private final SymbolTable symbolTable; 70 71 /** 72 * Contains a unique id. 73 */ 74 private int sectionId = -1; 75 76 /** 77 * Construct a {@code ByteContainer} object. 78 */ 79 public ByteContainer(String containerName, SymbolTable symbolTable) { 80 this.containerName = containerName; 81 this.symbolTable = symbolTable; 82 this.contentBytes = null; 83 this.bufferModified = false; 84 this.hasRelocations = false; 85 this.contentStream = new ByteArrayOutputStream(); 86 } 87 88 /** 89 * Update byte buffer to reflect the current contents of byte stream. 90 * 91 * @throws InternalError throws {@code InternalError} if buffer byte array was modified 92 */ 93 private void updateByteBuffer() { 94 if (!bufferModified) { 95 contentBytes = ByteBuffer.wrap(contentStream.toByteArray()); 96 // Default byte order of ByteBuffer is BIG_ENDIAN. 97 // Set it appropriately 98 this.contentBytes.order(ByteOrder.nativeOrder()); 99 } else { 100 throw new InternalError("Backing byte buffer no longer in sync with byte stream"); 101 } 102 } 103 104 /** 105 * Get the byte array of {@code ByteContainer}. 106 * 107 * @return byte array 108 * @throws InternalError throws {@code InternalError} if buffer byte array was modified 109 */ 110 public byte[] getByteArray() { 111 if (!bufferModified) { 112 updateByteBuffer(); 113 } 114 return contentBytes.array(); 115 } 116 117 /** 118 * Append to byte stream. It is an error to append to stream if the byte buffer version is 119 * changed. 120 * 121 * @param newBytes new content 122 * @param off offset start offset in {@code newBytes} 123 * @param len length of data to write 124 * @throws InternalError throws {@code InternalError} if buffer byte array was modified 125 */ 126 public ByteContainer appendBytes(byte[] newBytes, int off, int len) { 127 if (bufferModified) { 128 throw new InternalError("Backing byte buffer no longer in sync with byte stream"); 129 } 130 contentStream.write(newBytes, off, len); 131 return this; 132 } 133 134 public ByteContainer appendBytes(byte[] newBytes) { 135 appendBytes(newBytes, 0, newBytes.length); 136 return this; 137 } 138 139 public ByteContainer appendInt(int i) { 140 if (bufferModified) { 141 throw new InternalError("Backing byte buffer no longer in sync with byte stream"); 142 } 143 ByteBuffer b = ByteBuffer.allocate(Integer.BYTES); 144 b.order(ByteOrder.nativeOrder()); 145 b.putInt(i); 146 byte[] result = b.array(); 147 contentStream.write(result, 0, result.length); 148 return this; 149 } 150 151 public ByteContainer appendInts(int[] newInts) { 152 if (bufferModified) { 153 throw new InternalError("Backing byte buffer no longer in sync with byte stream"); 154 } 155 ByteBuffer b = ByteBuffer.allocate(Integer.BYTES * newInts.length).order(ByteOrder.nativeOrder()); 156 Arrays.stream(newInts).forEach(i -> b.putInt(i)); 157 byte[] result = b.array(); 158 contentStream.write(result, 0, result.length); 159 return this; 160 } 161 162 public void appendLong(long l) { 163 if (bufferModified) { 164 throw new InternalError("Backing byte buffer no longer in sync with byte stream"); 165 } 166 ByteBuffer b = ByteBuffer.allocate(8); 167 b.order(ByteOrder.nativeOrder()); 168 b.putLong(l); 169 byte[] result = b.array(); 170 contentStream.write(result, 0, result.length); 171 } 172 173 /** 174 * Return the current size of byte stream backing the BinaryContainer. 175 * 176 * @return size of buffer stream 177 */ 178 public int getByteStreamSize() { 179 return contentStream.size(); 180 } 181 182 /** 183 * Return the name of this container. 184 * 185 * @return string containing name 186 */ 187 public String getContainerName() { 188 return containerName; 189 } 190 191 /** 192 * Modify the byte buffer version of the byte output stream. Note that after calling this method 193 * all further updates to BinaryContainer will be out of sync with byte buffer content. 194 * 195 * @param index index of byte to be changed 196 * @param value new value 197 */ 198 public void putIntAt(int index, int value) { 199 if (!bufferModified) { 200 updateByteBuffer(); 201 } 202 contentBytes.putInt(index, value); 203 bufferModified = true; 204 } 205 206 public void putLongAt(int index, long value) { 207 if (!bufferModified) { 208 updateByteBuffer(); 209 } 210 contentBytes.putLong(index, value); 211 bufferModified = true; 212 } 213 214 public void setSectionId(int id) { 215 if (sectionId != -1) { 216 throw new InternalError("Assigning new sectionId (old: " + sectionId + ", new: " + id + ")"); 217 } 218 sectionId = id; 219 } 220 221 public int getSectionId() { 222 if (sectionId == -1) { 223 throw new InternalError("Using sectionId before assigned"); 224 } 225 return sectionId; 226 } 227 228 public Symbol createSymbol(int offset, Kind kind, Binding binding, int size, String name) { 229 Symbol symbol = new Symbol(offset, kind, binding, this, size, name); 230 symbolTable.addSymbol(symbol); 231 return symbol; 232 } 233 234 public GotSymbol createGotSymbol(String name) { 235 GotSymbol symbol = new GotSymbol(Kind.OBJECT, Binding.LOCAL, this, name); 236 symbolTable.addSymbol(symbol); 237 return symbol; 238 } 239 240 public GotSymbol createGotSymbol(int offset, String name) { 241 GotSymbol symbol = new GotSymbol(offset, Kind.OBJECT, Binding.LOCAL, this, name); 242 symbolTable.addSymbol(symbol); 243 return symbol; 244 } 245 246 public void clear() { 247 this.contentBytes = null; 248 this.contentStream = null; 249 } 250 251 public void setHasRelocations() { 252 this.hasRelocations = true; 253 } 254 255 public boolean hasRelocations() { 256 return this.hasRelocations; 257 } 258} 259