001/*
002 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.common.util;
024
025import static com.oracle.graal.compiler.common.util.TypeConversion.*;
026import jdk.internal.jvmci.common.*;
027import sun.misc.*;
028
029/**
030 * Provides low-level sequential write access to a byte[] array for signed and unsigned values of
031 * size 1, 2, 4, and 8 bytes. To avoid copying an array when the buffer size is no longer
032 * sufficient, the buffer is split into chunks of a fixed size.
033 *
034 * The flag {@code supportsUnalignedMemoryAccess} must be set according to the capabilities of the
035 * hardware architecture: the value {@code true} allows more efficient memory access on
036 * architectures that support unaligned memory accesses; the value {@code false} is the safe
037 * fallback that works on every hardware.
038 */
039public abstract class UnsafeArrayTypeWriter implements TypeWriter {
040
041    private static final int MIN_CHUNK_LENGTH = 200;
042    private static final int MAX_CHUNK_LENGTH = 16000;
043
044    static class Chunk {
045        protected final byte[] data;
046        protected int size;
047        protected Chunk next;
048
049        protected Chunk(int arrayLength) {
050            data = new byte[arrayLength];
051        }
052    }
053
054    protected final Chunk firstChunk;
055    protected Chunk writeChunk;
056    protected int totalSize;
057
058    public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) {
059        if (supportsUnalignedMemoryAccess) {
060            return new UnalignedUnsafeArrayTypeWriter();
061        } else {
062            return new AlignedUnsafeArrayTypeWriter();
063        }
064    }
065
066    protected UnsafeArrayTypeWriter() {
067        firstChunk = new Chunk(MIN_CHUNK_LENGTH);
068        writeChunk = firstChunk;
069    }
070
071    @Override
072    public final long getBytesWritten() {
073        return totalSize;
074    }
075
076    /**
077     * Copies the buffer into the provided byte[] array of length {@link #getBytesWritten()}.
078     */
079    public final byte[] toArray(byte[] result) {
080        assert result.length == totalSize;
081        int resultIdx = 0;
082        for (Chunk cur = firstChunk; cur != null; cur = cur.next) {
083            System.arraycopy(cur.data, 0, result, resultIdx, cur.size);
084            resultIdx += cur.size;
085        }
086        assert resultIdx == totalSize;
087        return result;
088    }
089
090    @Override
091    public final void putS1(long value) {
092        long offset = writeOffset(Byte.BYTES);
093        UnsafeAccess.unsafe.putByte(writeChunk.data, offset, asS1(value));
094    }
095
096    @Override
097    public final void putU1(long value) {
098        long offset = writeOffset(Byte.BYTES);
099        UnsafeAccess.unsafe.putByte(writeChunk.data, offset, asU1(value));
100    }
101
102    @Override
103    public final void putU2(long value) {
104        putS2(asU2(value));
105    }
106
107    @Override
108    public final void putU4(long value) {
109        putS4(asU4(value));
110    }
111
112    protected long writeOffset(int writeBytes) {
113        if (writeChunk.size + writeBytes >= writeChunk.data.length) {
114            Chunk newChunk = new Chunk(Math.min(writeChunk.data.length * 2, MAX_CHUNK_LENGTH));
115            writeChunk.next = newChunk;
116            writeChunk = newChunk;
117        }
118
119        assert Unsafe.ARRAY_BYTE_INDEX_SCALE == 1;
120        long result = writeChunk.size + Unsafe.ARRAY_BYTE_BASE_OFFSET;
121
122        totalSize += writeBytes;
123        writeChunk.size += writeBytes;
124        assert writeChunk.size <= writeChunk.data.length;
125
126        return result;
127    }
128}
129
130final class UnalignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
131    @Override
132    public void putS2(long value) {
133        long offset = writeOffset(Short.BYTES);
134        UnsafeAccess.unsafe.putShort(writeChunk.data, offset, asS2(value));
135    }
136
137    @Override
138    public void putS4(long value) {
139        long offset = writeOffset(Integer.BYTES);
140        UnsafeAccess.unsafe.putInt(writeChunk.data, offset, asS4(value));
141    }
142
143    @Override
144    public void putS8(long value) {
145        long offset = writeOffset(Long.BYTES);
146        UnsafeAccess.unsafe.putLong(writeChunk.data, offset, value);
147    }
148}
149
150final class AlignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
151    @Override
152    public void putS2(long value) {
153        long offset = writeOffset(Short.BYTES);
154        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
155        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
156    }
157
158    @Override
159    public void putS4(long value) {
160        long offset = writeOffset(Integer.BYTES);
161        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
162        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
163        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
164        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
165    }
166
167    @Override
168    public void putS8(long value) {
169        long offset = writeOffset(Long.BYTES);
170        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
171        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
172        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
173        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
174        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 4, (byte) (value >> 32));
175        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 5, (byte) (value >> 40));
176        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 6, (byte) (value >> 48));
177        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 7, (byte) (value >> 56));
178    }
179}