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 }