NativeOrderOutputStream.java revision 12408:777aaa19c4b1
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
24package jdk.tools.jaotc.utils;
25
26import java.io.ByteArrayOutputStream;
27import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
29import java.util.ArrayList;
30import java.util.List;
31
32public 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}
192