UnsafeArrayTypeWriter.java revision 12651:6ef01bd40ce2
1169092Sdeischen/*
2169092Sdeischen * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
3169092Sdeischen * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4164190Sjkoshy *
5164190Sjkoshy * This code is free software; you can redistribute it and/or modify it
6164190Sjkoshy * under the terms of the GNU General Public License version 2 only, as
7164190Sjkoshy * published by the Free Software Foundation.
8164190Sjkoshy *
9164190Sjkoshy * This code is distributed in the hope that it will be useful, but WITHOUT
10164190Sjkoshy * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11164190Sjkoshy * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12164190Sjkoshy * version 2 for more details (a copy is included in the LICENSE file that
13164190Sjkoshy * accompanied this code).
14164190Sjkoshy *
15164190Sjkoshy * You should have received a copy of the GNU General Public License version
16164190Sjkoshy * 2 along with this work; if not, write to the Free Software Foundation,
17164190Sjkoshy * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18164190Sjkoshy *
19164190Sjkoshy * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20164190Sjkoshy * or visit www.oracle.com if you need additional information or have any
21164190Sjkoshy * questions.
22164190Sjkoshy */
23164190Sjkoshypackage org.graalvm.compiler.core.common.util;
24164190Sjkoshy
25164190Sjkoshyimport static org.graalvm.compiler.core.common.util.TypeConversion.asS1;
26164190Sjkoshyimport static org.graalvm.compiler.core.common.util.TypeConversion.asS2;
27164190Sjkoshyimport static org.graalvm.compiler.core.common.util.TypeConversion.asS4;
28164190Sjkoshyimport static org.graalvm.compiler.core.common.util.TypeConversion.asU1;
29164190Sjkoshyimport static org.graalvm.compiler.core.common.util.TypeConversion.asU2;
30164190Sjkoshyimport static org.graalvm.compiler.core.common.util.TypeConversion.asU4;
31164190Sjkoshyimport sun.misc.Unsafe;
32164190Sjkoshy
33164190Sjkoshy/**
34164190Sjkoshy * Provides low-level sequential write access to a byte[] array for signed and unsigned values of
35164190Sjkoshy * size 1, 2, 4, and 8 bytes. To avoid copying an array when the buffer size is no longer
36164190Sjkoshy * sufficient, the buffer is split into chunks of a fixed size.
37164190Sjkoshy *
38164190Sjkoshy * The flag {@code supportsUnalignedMemoryAccess} must be set according to the capabilities of the
39164190Sjkoshy * hardware architecture: the value {@code true} allows more efficient memory access on
40164190Sjkoshy * architectures that support unaligned memory accesses; the value {@code false} is the safe
41164190Sjkoshy * fallback that works on every hardware.
42210345Skaiw */
43165535Sjkoshypublic abstract class UnsafeArrayTypeWriter implements TypeWriter {
44210345Skaiw
45164190Sjkoshy    private static final int MIN_CHUNK_LENGTH = 200;
46210345Skaiw    private static final int MAX_CHUNK_LENGTH = 16000;
47164190Sjkoshy
48164190Sjkoshy    static class Chunk {
49164190Sjkoshy        protected final byte[] data;
50164190Sjkoshy        protected int size;
51164190Sjkoshy        protected Chunk next;
52164190Sjkoshy
53164190Sjkoshy        protected Chunk(int arrayLength) {
54164190Sjkoshy            data = new byte[arrayLength];
55164190Sjkoshy        }
56164190Sjkoshy    }
57164190Sjkoshy
58164190Sjkoshy    protected final Chunk firstChunk;
59164190Sjkoshy    protected Chunk writeChunk;
60164190Sjkoshy    protected int totalSize;
61164190Sjkoshy
62164190Sjkoshy    public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) {
63164190Sjkoshy        if (supportsUnalignedMemoryAccess) {
64164190Sjkoshy            return new UnalignedUnsafeArrayTypeWriter();
65164190Sjkoshy        } else {
66164190Sjkoshy            return new AlignedUnsafeArrayTypeWriter();
67164190Sjkoshy        }
68164190Sjkoshy    }
69164190Sjkoshy
70164190Sjkoshy    protected UnsafeArrayTypeWriter() {
71164190Sjkoshy        firstChunk = new Chunk(MIN_CHUNK_LENGTH);
72164190Sjkoshy        writeChunk = firstChunk;
73164190Sjkoshy    }
74164190Sjkoshy
75164190Sjkoshy    @Override
76164190Sjkoshy    public final long getBytesWritten() {
77164190Sjkoshy        return totalSize;
78164190Sjkoshy    }
79164190Sjkoshy
80164190Sjkoshy    /**
81164190Sjkoshy     * Copies the buffer into the provided byte[] array of length {@link #getBytesWritten()}.
82164190Sjkoshy     */
83164190Sjkoshy    public final byte[] toArray(byte[] result) {
84164190Sjkoshy        assert result.length == totalSize;
85164190Sjkoshy        int resultIdx = 0;
86164190Sjkoshy        for (Chunk cur = firstChunk; cur != null; cur = cur.next) {
87164190Sjkoshy            System.arraycopy(cur.data, 0, result, resultIdx, cur.size);
88164190Sjkoshy            resultIdx += cur.size;
89164190Sjkoshy        }
90164190Sjkoshy        assert resultIdx == totalSize;
91164190Sjkoshy        return result;
92164190Sjkoshy    }
93164190Sjkoshy
94164190Sjkoshy    @Override
95164190Sjkoshy    public final void putS1(long value) {
96169092Sdeischen        long offset = writeOffset(Byte.BYTES);
97169092Sdeischen        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset, asS1(value));
98169092Sdeischen    }
99169092Sdeischen
100164190Sjkoshy    @Override
101164190Sjkoshy    public final void putU1(long value) {
102164190Sjkoshy        long offset = writeOffset(Byte.BYTES);
103164190Sjkoshy        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset, asU1(value));
104164190Sjkoshy    }
105164190Sjkoshy
106    @Override
107    public final void putU2(long value) {
108        putS2(asU2(value));
109    }
110
111    @Override
112    public final void putU4(long value) {
113        putS4(asU4(value));
114    }
115
116    protected long writeOffset(int writeBytes) {
117        if (writeChunk.size + writeBytes >= writeChunk.data.length) {
118            Chunk newChunk = new Chunk(Math.min(writeChunk.data.length * 2, MAX_CHUNK_LENGTH));
119            writeChunk.next = newChunk;
120            writeChunk = newChunk;
121        }
122
123        assert Unsafe.ARRAY_BYTE_INDEX_SCALE == 1;
124        long result = writeChunk.size + Unsafe.ARRAY_BYTE_BASE_OFFSET;
125
126        totalSize += writeBytes;
127        writeChunk.size += writeBytes;
128        assert writeChunk.size <= writeChunk.data.length;
129
130        return result;
131    }
132}
133
134final class UnalignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
135    @Override
136    public void putS2(long value) {
137        long offset = writeOffset(Short.BYTES);
138        UnsafeAccess.UNSAFE.putShort(writeChunk.data, offset, asS2(value));
139    }
140
141    @Override
142    public void putS4(long value) {
143        long offset = writeOffset(Integer.BYTES);
144        UnsafeAccess.UNSAFE.putInt(writeChunk.data, offset, asS4(value));
145    }
146
147    @Override
148    public void putS8(long value) {
149        long offset = writeOffset(Long.BYTES);
150        UnsafeAccess.UNSAFE.putLong(writeChunk.data, offset, value);
151    }
152}
153
154final class AlignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
155    @Override
156    public void putS2(long value) {
157        long offset = writeOffset(Short.BYTES);
158        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
159        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
160    }
161
162    @Override
163    public void putS4(long value) {
164        long offset = writeOffset(Integer.BYTES);
165        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
166        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
167        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
168        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
169    }
170
171    @Override
172    public void putS8(long value) {
173        long offset = writeOffset(Long.BYTES);
174        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
175        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
176        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
177        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
178        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 4, (byte) (value >> 32));
179        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 5, (byte) (value >> 40));
180        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 6, (byte) (value >> 48));
181        UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 7, (byte) (value >> 56));
182    }
183}
184