1 /*
   2  * Copyright (c) 2015, 2018, 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 
  24 
  25 package org.graalvm.compiler.core.common.util;
  26 
  27 import static org.graalvm.compiler.core.common.util.TypeConversion.asS1;
  28 import static org.graalvm.compiler.core.common.util.TypeConversion.asS2;
  29 import static org.graalvm.compiler.core.common.util.TypeConversion.asS4;
  30 import static org.graalvm.compiler.core.common.util.TypeConversion.asU1;
  31 import static org.graalvm.compiler.core.common.util.TypeConversion.asU2;
  32 import static org.graalvm.compiler.core.common.util.TypeConversion.asU4;
  33 import sun.misc.Unsafe;
  34 
  35 /**
  36  * Provides low-level sequential write access to a byte[] array for signed and unsigned values of
  37  * size 1, 2, 4, and 8 bytes. To avoid copying an array when the buffer size is no longer
  38  * sufficient, the buffer is split into chunks of a fixed size.
  39  *
  40  * The flag {@code supportsUnalignedMemoryAccess} must be set according to the capabilities of the
  41  * hardware architecture: the value {@code true} allows more efficient memory access on
  42  * architectures that support unaligned memory accesses; the value {@code false} is the safe
  43  * fallback that works on every hardware.
  44  */
  45 public abstract class UnsafeArrayTypeWriter implements TypeWriter {
  46 
  47     private static final int MIN_CHUNK_LENGTH = 200;
  48     private static final int MAX_CHUNK_LENGTH = 16000;
  49 
  50     static class Chunk {
  51         protected final byte[] data;
  52         protected int size;
  53         protected Chunk next;
  54 
  55         protected Chunk(int arrayLength) {
  56             data = new byte[arrayLength];
  57         }
  58     }
  59 
  60     protected final Chunk firstChunk;
  61     protected Chunk writeChunk;
  62     protected int totalSize;
  63 
  64     public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) {
  65         if (supportsUnalignedMemoryAccess) {
  66             return new UnalignedUnsafeArrayTypeWriter();
  67         } else {
  68             return new AlignedUnsafeArrayTypeWriter();
  69         }
  70     }
  71 
  72     protected UnsafeArrayTypeWriter() {
  73         firstChunk = new Chunk(MIN_CHUNK_LENGTH);
  74         writeChunk = firstChunk;
  75     }
  76 
  77     @Override
  78     public final long getBytesWritten() {
  79         return totalSize;
  80     }
  81 
  82     /**
  83      * Copies the buffer into the provided byte[] array of length {@link #getBytesWritten()}.
  84      */
  85     public final byte[] toArray(byte[] result) {
  86         assert result.length == totalSize;
  87         int resultIdx = 0;
  88         for (Chunk cur = firstChunk; cur != null; cur = cur.next) {
  89             System.arraycopy(cur.data, 0, result, resultIdx, cur.size);
  90             resultIdx += cur.size;
  91         }
  92         assert resultIdx == totalSize;
  93         return result;
  94     }
  95 
  96     @Override
  97     public final void putS1(long value) {
  98         long offset = writeOffset(Byte.BYTES);
  99         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset, asS1(value));
 100     }
 101 
 102     @Override
 103     public final void putU1(long value) {
 104         long offset = writeOffset(Byte.BYTES);
 105         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset, asU1(value));
 106     }
 107 
 108     @Override
 109     public final void putU2(long value) {
 110         putS2(asU2(value));
 111     }
 112 
 113     @Override
 114     public final void putU4(long value) {
 115         putS4(asU4(value));
 116     }
 117 
 118     protected long writeOffset(int writeBytes) {
 119         if (writeChunk.size + writeBytes >= writeChunk.data.length) {
 120             Chunk newChunk = new Chunk(Math.min(writeChunk.data.length * 2, MAX_CHUNK_LENGTH));
 121             writeChunk.next = newChunk;
 122             writeChunk = newChunk;
 123         }
 124 
 125         assert Unsafe.ARRAY_BYTE_INDEX_SCALE == 1;
 126         long result = writeChunk.size + Unsafe.ARRAY_BYTE_BASE_OFFSET;
 127 
 128         totalSize += writeBytes;
 129         writeChunk.size += writeBytes;
 130         assert writeChunk.size <= writeChunk.data.length;
 131 
 132         return result;
 133     }
 134 }
 135 
 136 final class UnalignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
 137     @Override
 138     public void putS2(long value) {
 139         long offset = writeOffset(Short.BYTES);
 140         UnsafeAccess.UNSAFE.putShort(writeChunk.data, offset, asS2(value));
 141     }
 142 
 143     @Override
 144     public void putS4(long value) {
 145         long offset = writeOffset(Integer.BYTES);
 146         UnsafeAccess.UNSAFE.putInt(writeChunk.data, offset, asS4(value));
 147     }
 148 
 149     @Override
 150     public void putS8(long value) {
 151         long offset = writeOffset(Long.BYTES);
 152         UnsafeAccess.UNSAFE.putLong(writeChunk.data, offset, value);
 153     }
 154 }
 155 
 156 final class AlignedUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter {
 157     @Override
 158     public void putS2(long value) {
 159         long offset = writeOffset(Short.BYTES);
 160         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
 161         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
 162     }
 163 
 164     @Override
 165     public void putS4(long value) {
 166         long offset = writeOffset(Integer.BYTES);
 167         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
 168         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
 169         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
 170         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
 171     }
 172 
 173     @Override
 174     public void putS8(long value) {
 175         long offset = writeOffset(Long.BYTES);
 176         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 0, (byte) (value >> 0));
 177         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 1, (byte) (value >> 8));
 178         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 2, (byte) (value >> 16));
 179         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 3, (byte) (value >> 24));
 180         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 4, (byte) (value >> 32));
 181         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 5, (byte) (value >> 40));
 182         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 6, (byte) (value >> 48));
 183         UnsafeAccess.UNSAFE.putByte(writeChunk.data, offset + 7, (byte) (value >> 56));
 184     }
 185 }