1 /*
2  * Copyright (c) 2015, 2019, 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 package org.graalvm.compiler.core.common.util;
26 
27 import static org.graalvm.compiler.core.common.util.TypeConversion.asS1;
28 import static org.graalvm.compiler.core.common.util.TypeConversion.asS2;
29 import static org.graalvm.compiler.core.common.util.TypeConversion.asS4;
30 import static org.graalvm.compiler.core.common.util.TypeConversion.asU1;
31 import static org.graalvm.compiler.core.common.util.TypeConversion.asU2;
32 import static org.graalvm.compiler.core.common.util.TypeConversion.asU4;
33 import static org.graalvm.compiler.serviceprovider.GraalUnsafeAccess.getUnsafe;
34 
35 import java.nio.ByteBuffer;
36 
37 import org.graalvm.compiler.core.common.calc.UnsignedMath;
38 
39 import sun.misc.Unsafe;
40 
41 /**
42  * Provides low-level sequential write access to a byte[] array for signed and unsigned values of
43  * size 1, 2, 4, and 8 bytes. To avoid copying an array when the buffer size is no longer
44  * sufficient, the buffer is split into chunks of a fixed size.
45  *
46  * The flag {@code supportsUnalignedMemoryAccess} must be set according to the capabilities of the
47  * hardware architecture: the value {@code true} allows more efficient memory access on
48  * architectures that support unaligned memory accesses; the value {@code false} is the safe
49  * fallback that works on every hardware.
50  */
51 public abstract class UnsafeArrayTypeWriter implements TypeWriter {
52     private static final Unsafe UNSAFE = getUnsafe();
53     private static final int MIN_CHUNK_LENGTH = 200;
54     private static final int MAX_CHUNK_LENGTH = 16000;
55 
56     // Constants for UNSIGNED5 coding of Pack200
57     public static final long HIGH_WORD_SHIFT = 6;
58     public static final long NUM_HIGH_CODES = 1 << HIGH_WORD_SHIFT; // number of high codes (64)
59     public static final long NUM_LOW_CODES = (1 << Byte.SIZE) - NUM_HIGH_CODES;
60     public static final long MAX_BYTES = 11;
61 
62     static class Chunk {
63         protected final byte[] data;
64         protected int size;
65         protected Chunk next;
66 
Chunk(int arrayLength)67         protected Chunk(int arrayLength) {
68             data = new byte[arrayLength];
69         }
70     }
71 
72     protected final Chunk firstChunk;
73     protected Chunk writeChunk;
74     protected int totalSize;
75 
create(boolean supportsUnalignedMemoryAccess)76     public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) {
77         if (supportsUnalignedMemoryAccess) {
78             return new UnalignedUnsafeArrayTypeWriter();
79         } else {
80             return new AlignedUnsafeArrayTypeWriter();
81         }
82     }
83 
UnsafeArrayTypeWriter()84     protected UnsafeArrayTypeWriter() {
85         firstChunk = new Chunk(MIN_CHUNK_LENGTH);
86         writeChunk = firstChunk;
87     }
88 
89     @Override
getBytesWritten()90     public final long getBytesWritten() {
91         return totalSize;
92     }
93 
94     /**
95      * Copies the buffer into the provided byte[] array of length {@link #getBytesWritten()}.
96      */
toArray(byte[] result)97     public final byte[] toArray(byte[] result) {
98         assert result.length == totalSize;
99         int resultIdx = 0;
100         for (Chunk cur = firstChunk; cur != null; cur = cur.next) {
101             System.arraycopy(cur.data, 0, result, resultIdx, cur.size);
102             resultIdx += cur.size;
103         }
104         assert resultIdx == totalSize;
105         return result;
106     }
107 
108     /** Copies the buffer into the provided ByteBuffer at its current position. */
toByteBuffer(ByteBuffer buffer)109     public final ByteBuffer toByteBuffer(ByteBuffer buffer) {
110         assert buffer.remaining() <= totalSize;
111         int initialPos = buffer.position();
112         for (Chunk cur = firstChunk; cur != null; cur = cur.next) {
113             buffer.put(cur.data, 0, cur.size);
114         }
115         assert buffer.position() - initialPos == totalSize;
116         return buffer;
117     }
118 
toArray()119     public final byte[] toArray() {
120         byte[] result = new byte[TypeConversion.asS4(getBytesWritten())];
121         return toArray(result);
122     }
123 
124     @Override
putS1(long value)125     public final void putS1(long value) {
126         long offset = writeOffset(Byte.BYTES);
127         UNSAFE.putByte(writeChunk.data, offset, asS1(value));
128     }
129 
130     @Override
putU1(long value)131     public final void putU1(long value) {
132         long offset = writeOffset(Byte.BYTES);
133         UNSAFE.putByte(writeChunk.data, offset, asU1(value));
134     }
135 
136     @Override
putU2(long value)137     public final void putU2(long value) {
138         putS2(asU2(value));
139     }
140 
141     @Override
putU4(long value)142     public final void putU4(long value) {
143         putS4(asU4(value));
144     }
145 
146     @Override
putS2(long value)147     public void putS2(long value) {
148         long offset = writeOffset(Short.BYTES);
149         putS2(value, writeChunk, offset);
150     }
151 
152     @Override
putS4(long value)153     public void putS4(long value) {
154         long offset = writeOffset(Integer.BYTES);
155         putS4(value, writeChunk, offset);
156     }
157 
158     @Override
putS8(long value)159     public void putS8(long value) {
160         long offset = writeOffset(Long.BYTES);
161         putS8(value, writeChunk, offset);
162     }
163 
putS2(long value, Chunk chunk, long offset)164     protected abstract void putS2(long value, Chunk chunk, long offset);
165 
putS4(long value, Chunk chunk, long offset)166     protected abstract void putS4(long value, Chunk chunk, long offset);
167 
putS8(long value, Chunk chunk, long offset)168     protected abstract void putS8(long value, Chunk chunk, long offset);
169 
writeOffset(int writeBytes)170     protected long writeOffset(int writeBytes) {
171         if (writeChunk.size + writeBytes >= writeChunk.data.length) {
172             Chunk newChunk = new Chunk(Math.min(writeChunk.data.length * 2, MAX_CHUNK_LENGTH));
173             writeChunk.next = newChunk;
174             writeChunk = newChunk;
175         }
176 
177         assert Unsafe.ARRAY_BYTE_INDEX_SCALE == 1;
178         long result = writeChunk.size + Unsafe.ARRAY_BYTE_BASE_OFFSET;
179 
180         totalSize += writeBytes;
181         writeChunk.size += writeBytes;
182         assert writeChunk.size <= writeChunk.data.length;
183 
184         return result;
185     }
186 
187     @Override
patchS4(long value, long offset)188     public void patchS4(long value, long offset) {
189         long chunkStartOffset = 0;
190         Chunk chunk = firstChunk;
191         while (chunkStartOffset + chunk.size <= offset) {
192             chunkStartOffset += chunk.size;
193             chunk = chunk.next;
194         }
195 
196         long targetOffset = offset - chunkStartOffset;
197         assert targetOffset + Integer.BYTES <= chunk.size : "out of bounds";
198         putS4(value, chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + targetOffset);
199     }
200 
201     @Override
putSV(long value)202     public void putSV(long value) {
203         // this is a modified version of the SIGNED5 encoding from Pack200
204         write(encodeSign(value));
205     }
206 
207     @Override
putUV(long value)208     public void putUV(long value) {
209         // this is a modified version of the UNSIGNED5 encoding from Pack200
210         write(value);
211     }
212 
encodeSign(long value)213     private static long encodeSign(long value) {
214         return (value << 1) ^ (value >> 63);
215     }
216 
write(long value)217     private void write(long value) {
218         if (UnsignedMath.belowThan(value, NUM_LOW_CODES)) {
219             putU1(value);
220         } else {
221             writePacked(value);
222         }
223     }
224 
writePacked(long value)225     private void writePacked(long value) {
226         long sum = value;
227         for (int i = 1; UnsignedMath.aboveOrEqual(sum, NUM_LOW_CODES) && i < MAX_BYTES; i++) {
228             sum -= NUM_LOW_CODES;
229             long u1 = NUM_LOW_CODES + (sum & (NUM_HIGH_CODES - 1)); // this is a "high code"
230             sum >>>= HIGH_WORD_SHIFT; // extracted 6 bits
231             putU1(u1);
232         }
233 
234         // remainder is either a "low code" or the last byte
235         assert sum == (sum & 0xFF) : "not a byte";
236         putU1(sum & 0xFF);
237     }
238 }
239 
240 final class UnalignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
241     private static final Unsafe UNSAFE = getUnsafe();
242 
243     @Override
putS2(long value, Chunk chunk, long offset)244     protected void putS2(long value, Chunk chunk, long offset) {
245         UNSAFE.putShort(chunk.data, offset, asS2(value));
246     }
247 
248     @Override
putS4(long value, Chunk chunk, long offset)249     protected void putS4(long value, Chunk chunk, long offset) {
250         UNSAFE.putInt(chunk.data, offset, asS4(value));
251     }
252 
253     @Override
putS8(long value, Chunk chunk, long offset)254     protected void putS8(long value, Chunk chunk, long offset) {
255         UNSAFE.putLong(chunk.data, offset, value);
256     }
257 }
258 
259 final class AlignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
260     private static final Unsafe UNSAFE = getUnsafe();
261 
262     @Override
putS2(long value, Chunk chunk, long offset)263     protected void putS2(long value, Chunk chunk, long offset) {
264         UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 0));
265         UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 8));
266     }
267 
268     @Override
putS4(long value, Chunk chunk, long offset)269     protected void putS4(long value, Chunk chunk, long offset) {
270         UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 0));
271         UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 8));
272         UNSAFE.putByte(chunk.data, offset + 2, (byte) (value >> 16));
273         UNSAFE.putByte(chunk.data, offset + 3, (byte) (value >> 24));
274     }
275 
276     @Override
putS8(long value, Chunk chunk, long offset)277     protected void putS8(long value, Chunk chunk, long offset) {
278         UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 0));
279         UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 8));
280         UNSAFE.putByte(chunk.data, offset + 2, (byte) (value >> 16));
281         UNSAFE.putByte(chunk.data, offset + 3, (byte) (value >> 24));
282         UNSAFE.putByte(chunk.data, offset + 4, (byte) (value >> 32));
283         UNSAFE.putByte(chunk.data, offset + 5, (byte) (value >> 40));
284         UNSAFE.putByte(chunk.data, offset + 6, (byte) (value >> 48));
285         UNSAFE.putByte(chunk.data, offset + 7, (byte) (value >> 56));
286     }
287 }
288