view graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/UnsafeArrayTypeWriter.java @ 21541:5e868236654f

moved UnsafeAccess to com.oracle.jvmci.common (JBS:GRAAL-53)
author Doug Simon <doug.simon@oracle.com>
date Mon, 25 May 2015 22:17:10 +0200
parents acc86d08e1cc
children
line wrap: on
line source

/*
 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.graal.compiler.common.util;

import static com.oracle.graal.compiler.common.util.TypeConversion.*;
import sun.misc.*;

import com.oracle.jvmci.common.*;

/**
 * Provides low-level sequential write access to a byte[] array for signed and unsigned values of
 * size 1, 2, 4, and 8 bytes. To avoid copying an array when the buffer size is no longer
 * sufficient, the buffer is split into chunks of a fixed size.
 *
 * The flag {@code supportsUnalignedMemoryAccess} must be set according to the capabilities of the
 * hardware architecture: the value {@code true} allows more efficient memory access on
 * architectures that support unaligned memory accesses; the value {@code false} is the safe
 * fallback that works on every hardware.
 */
public abstract class UnsafeArrayTypeWriter implements TypeWriter {

    private static final int MIN_CHUNK_LENGTH = 200;
    private static final int MAX_CHUNK_LENGTH = 16000;

    static class Chunk {
        protected final byte[] data;
        protected int size;
        protected Chunk next;

        protected Chunk(int arrayLength) {
            data = new byte[arrayLength];
        }
    }

    protected final Chunk firstChunk;
    protected Chunk writeChunk;
    protected int totalSize;

    public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) {
        if (supportsUnalignedMemoryAccess) {
            return new UnalignedUnsafeArrayTypeWriter();
        } else {
            return new AlignedUnsafeArrayTypeWriter();
        }
    }

    protected UnsafeArrayTypeWriter() {
        firstChunk = new Chunk(MIN_CHUNK_LENGTH);
        writeChunk = firstChunk;
    }

    @Override
    public final long getBytesWritten() {
        return totalSize;
    }

    /**
     * Copies the buffer into the provided byte[] array of length {@link #getBytesWritten()}.
     */
    public final byte[] toArray(byte[] result) {
        assert result.length == totalSize;
        int resultIdx = 0;
        for (Chunk cur = firstChunk; cur != null; cur = cur.next) {
            System.arraycopy(cur.data, 0, result, resultIdx, cur.size);
            resultIdx += cur.size;
        }
        assert resultIdx == totalSize;
        return result;
    }

    @Override
    public final void putS1(long value) {
        long offset = writeOffset(Byte.BYTES);
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset, asS1(value));
    }

    @Override
    public final void putU1(long value) {
        long offset = writeOffset(Byte.BYTES);
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset, asU1(value));
    }

    @Override
    public final void putU2(long value) {
        putS2(asU2(value));
    }

    @Override
    public final void putU4(long value) {
        putS4(asU4(value));
    }

    protected long writeOffset(int writeBytes) {
        if (writeChunk.size + writeBytes >= writeChunk.data.length) {
            Chunk newChunk = new Chunk(Math.min(writeChunk.data.length * 2, MAX_CHUNK_LENGTH));
            writeChunk.next = newChunk;
            writeChunk = newChunk;
        }

        assert Unsafe.ARRAY_BYTE_INDEX_SCALE == 1;
        long result = writeChunk.size + Unsafe.ARRAY_BYTE_BASE_OFFSET;

        totalSize += writeBytes;
        writeChunk.size += writeBytes;
        assert writeChunk.size <= writeChunk.data.length;

        return result;
    }
}

final class UnalignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
    @Override
    public void putS2(long value) {
        long offset = writeOffset(Short.BYTES);
        UnsafeAccess.unsafe.putShort(writeChunk.data, offset, asS2(value));
    }

    @Override
    public void putS4(long value) {
        long offset = writeOffset(Integer.BYTES);
        UnsafeAccess.unsafe.putInt(writeChunk.data, offset, asS4(value));
    }

    @Override
    public void putS8(long value) {
        long offset = writeOffset(Long.BYTES);
        UnsafeAccess.unsafe.putLong(writeChunk.data, offset, value);
    }
}

final class AlignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
    @Override
    public void putS2(long value) {
        long offset = writeOffset(Short.BYTES);
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
    }

    @Override
    public void putS4(long value) {
        long offset = writeOffset(Integer.BYTES);
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
    }

    @Override
    public void putS8(long value) {
        long offset = writeOffset(Long.BYTES);
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 4, (byte) (value >> 32));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 5, (byte) (value >> 40));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 6, (byte) (value >> 48));
        UnsafeAccess.unsafe.putByte(writeChunk.data, offset + 7, (byte) (value >> 56));
    }
}