1 /* 2 * Copyright (c) 2016, 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 package jdk.tools.jaotc.utils; 25 26 import java.io.ByteArrayOutputStream; 27 import java.nio.ByteBuffer; 28 import java.nio.ByteOrder; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 public class NativeOrderOutputStream { 33 private final PatchableByteOutputStream os = new PatchableByteOutputStream(); 34 private final byte[] backingArray = new byte[8]; 35 private final ByteBuffer buffer; 36 private final List<Patchable> patches = new ArrayList<>(); 37 private int size; 38 39 public NativeOrderOutputStream() { 40 buffer = ByteBuffer.wrap(backingArray); 41 buffer.order(ByteOrder.nativeOrder()); 42 } 43 44 public static int alignUp(int value, int alignment) { 45 if (Integer.bitCount(alignment) != 1) { 46 throw new IllegalArgumentException("Must be a power of 2"); 47 } 48 49 int aligned = (value + (alignment - 1)) & -alignment; 50 51 if (aligned < value || (aligned & (alignment - 1)) != 0) { 52 throw new RuntimeException("Error aligning: " + value + " -> " + aligned); 53 } 54 return aligned; 55 } 56 57 public NativeOrderOutputStream putLong(long value) { 58 fillLong(value); 59 os.write(backingArray, 0, 8); 60 size += 8; 61 return this; 62 } 63 64 public NativeOrderOutputStream putInt(int value) { 65 fillInt(value); 66 os.write(backingArray, 0, 4); 67 size += 4; 68 return this; 69 } 70 71 public NativeOrderOutputStream align(int alignment) { 72 int aligned = alignUp(position(), alignment); 73 74 int diff = aligned - position(); 75 if (diff > 0) { 76 byte[] b = new byte[diff]; 77 os.write(b, 0, b.length); 78 size += diff; 79 } 80 81 assert aligned == position(); 82 return this; 83 } 84 85 public int position() { 86 return os.size(); 87 } 88 89 private void fillInt(int value) { 90 buffer.putInt(0, value); 91 } 92 93 private void fillLong(long value) { 94 buffer.putLong(0, value); 95 } 96 97 private NativeOrderOutputStream putAt(byte[] data, int length, int position) { 98 os.writeAt(data, length, position); 99 return this; 100 } 101 102 public NativeOrderOutputStream put(byte[] data) { 103 os.write(data, 0, data.length); 104 size += data.length; 105 return this; 106 } 107 108 public byte[] array() { 109 checkPatches(); 110 byte[] bytes = os.toByteArray(); 111 assert size == bytes.length; 112 return bytes; 113 } 114 115 private void checkPatches() { 116 for (Patchable patch : patches) { 117 if (!patch.patched()) { 118 throw new RuntimeException("Not all patches patched, missing at offset: " + patch); 119 } 120 } 121 } 122 123 public PatchableInt patchableInt() { 124 int position = os.size(); 125 PatchableInt patchableInt = new PatchableInt(position); 126 putInt(0); 127 patches.add(patchableInt); 128 return patchableInt; 129 } 130 131 public abstract class Patchable { 132 private final int position; 133 private boolean patched = false; 134 135 Patchable(int position) { 136 this.position = position; 137 } 138 139 protected boolean patched() { 140 return patched; 141 } 142 143 protected void patch(int length) { 144 putAt(backingArray, length, position); 145 patched = true; 146 } 147 148 public int position() { 149 return position; 150 } 151 } 152 153 public class PatchableInt extends Patchable { 154 private int value = 0; 155 156 public PatchableInt(int position) { 157 super(position); 158 } 159 160 public void set(int value) { 161 this.value = value; 162 fillInt(value); 163 patch(4); 164 } 165 166 public int value() { 167 return value; 168 } 169 170 @Override 171 public String toString() { 172 final StringBuilder sb = new StringBuilder("PatchableInt{"); 173 sb.append("position=").append(super.position()).append(", "); 174 sb.append("patched=").append(patched()).append(", "); 175 sb.append("value=").append(value); 176 sb.append('}'); 177 return sb.toString(); 178 } 179 } 180 181 private static class PatchableByteOutputStream extends ByteArrayOutputStream { 182 183 public void writeAt(byte[] data, int length, int position) { 184 long end = (long)position + (long)length; 185 if (buf.length < end) { 186 throw new IllegalArgumentException("Array not properly sized"); 187 } 188 System.arraycopy(data, 0, buf, position, length); 189 } 190 } 191 }