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