--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java 2017-02-15 16:57:18.099201468 -0800 @@ -0,0 +1,183 @@ +/* + * 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 org.graalvm.compiler.core.common.util; + +import static org.graalvm.compiler.core.common.util.TypeConversion.asS1; +import static org.graalvm.compiler.core.common.util.TypeConversion.asS2; +import static org.graalvm.compiler.core.common.util.TypeConversion.asS4; +import static org.graalvm.compiler.core.common.util.TypeConversion.asU1; +import static org.graalvm.compiler.core.common.util.TypeConversion.asU2; +import static org.graalvm.compiler.core.common.util.TypeConversion.asU4; +import sun.misc.Unsafe; + +/** + * 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)); + } +}