1 /*
   2  * Copyright (c) 2015, 2015, 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 package org.graalvm.compiler.core.common.util;
  24 
  25 import static org.graalvm.compiler.core.common.util.TypeConversion.asS1;
  26 import static org.graalvm.compiler.core.common.util.TypeConversion.asS2;
  27 import static org.graalvm.compiler.core.common.util.TypeConversion.asS4;
  28 import static org.graalvm.compiler.core.common.util.TypeConversion.asU1;
  29 import static org.graalvm.compiler.core.common.util.TypeConversion.asU2;
  30 import static org.graalvm.compiler.core.common.util.TypeConversion.asU4;
  31 import sun.misc.Unsafe;
  32 
  33 /**
  34  * Provides low-level sequential write access to a byte[] array for signed and unsigned values of
  35  * size 1, 2, 4, and 8 bytes. To avoid copying an array when the buffer size is no longer
  36  * sufficient, the buffer is split into chunks of a fixed size.
  37  *
  38  * The flag {@code supportsUnalignedMemoryAccess} must be set according to the capabilities of the
  39  * hardware architecture: the value {@code true} allows more efficient memory access on
  40  * architectures that support unaligned memory accesses; the value {@code false} is the safe
  41  * fallback that works on every hardware.
  42  */
  43 public abstract class UnsafeArrayTypeWriter implements TypeWriter {
  44 
  45     private static final int MIN_CHUNK_LENGTH = 200;
  46     private static final int MAX_CHUNK_LENGTH = 16000;
  47 
  48     static class Chunk {
  49         protected final byte[] data;
  50         protected int size;
  51         protected Chunk next;
  52 
  53         protected Chunk(int arrayLength) {
  54             data = new byte[arrayLength];
  55         }
  56     }
  57 
  58     protected final Chunk firstChunk;
  59     protected Chunk writeChunk;
  60     protected int totalSize;
  61 
  62     public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) {
  63         if (supportsUnalignedMemoryAccess) {
  64             return new UnalignedUnsafeArrayTypeWriter();
  65         } else {
  66             return new AlignedUnsafeArrayTypeWriter();
  67         }
  68     }
  69 
  70     protected UnsafeArrayTypeWriter() {
  71         firstChunk = new Chunk(MIN_CHUNK_LENGTH);
  72         writeChunk = firstChunk;
  73     }
  74 
  75     @Override
  76     public final long getBytesWritten() {
  77         return totalSize;
  78     }
  79 
  80     /**
  81      * Copies the buffer into the provided byte[] array of length {@link #getBytesWritten()}.
  82      */
  83     public final byte[] toArray(byte[] result) {
  84         assert result.length == totalSize;
  85         int resultIdx = 0;
  86         for (Chunk cur = firstChunk; cur != null; cur = cur.next) {
  87             System.arraycopy(cur.data, 0, result, resultIdx, cur.size);
  88             resultIdx += cur.size;
  89         }
  90         assert resultIdx == totalSize;
  91         return result;
  92     }
  93 
  94     @Override
  95     public final void putS1(long value) {
  96         long offset = writeOffset(Byte.BYTES);
  97         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset, asS1(value));
  98     }
  99 
 100     @Override
 101     public final void putU1(long value) {
 102         long offset = writeOffset(Byte.BYTES);
 103         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset, asU1(value));
 104     }
 105 
 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 
 134 final 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 
 154 final 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 }