[SOLVED] What is the most efficient way to reinterpret underlying bit patterns and write it into and array or field?

Issue

Using Unsafe.putXXX one can put primitive type into an array or object field.

But code like the following generate errors.

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Arrays;

public class Main {

  public static void main(String[] args) {
    VarHandle varHandle = MethodHandles.arrayElementVarHandle(long[].class);

    byte[] array = new byte[32];

    printArray(array);
    varHandle.set(array, 1, 5);
    printArray(array);

    System.out.println(varHandle.get(array, 1));
  }

  private static void printArray(byte[] array) {
    System.out.println(Arrays.toString(array));
  }

}
Exception in thread "main" java.lang.ClassCastException: Cannot cast [B to [J
    at java.base/java.lang.Class.cast(Class.java:3780)
    at Main.main(Main.java:15)

Also the bytes may be write as:

byte[] array = new byte[32];
long v = 5,
int i = 8;
int high = (int) (v >>> 32);
int low = (int) v;
array[i + 0] = (byte) (high >>> 24);
array[i + 1] = (byte) (high >>> 16);
array[i + 2] = (byte) (high >>> 8);
array[i + 3] = (byte) high;
array[i + 4] = (byte) (low >>> 24);
array[i + 5] = (byte) (low >>> 16);
array[i + 6] = (byte) (low >>> 8);
array[i + 7] = (byte) low;

Is there an efficient way to write reinterpret different types and write them into fields and arrays possibly avoiding Unsafe but as efficient.

Any special cases where the compiler or JIT will recognise the intent and optimise accordingly.

Solution

For byte[] in particular you can use MethodHandles::byteArrayViewVarHandle:

public static void main(String[] args) {
    VarHandle varHandle = MethodHandles.byteArrayViewVarHandle(long[].class,
                                                               ByteOrder.nativeOrder());

    byte[] array = new byte[32];

    printArray(array);
    varHandle.set(array, 1, 5);
    printArray(array);

    System.out.println(varHandle.get(array, 1));
}

private static void printArray(byte[] array) {
    System.out.println(Arrays.toString(array));
}

There are some hoops you have to jump through with VarHandles to make them just as fast as Unsafe;

  1. Make sure the VarHandle itself is constant, this can be done by putting it in a static final field and accessing it from there.
  2. Make sure the VarHandle invocation is exact. Here this would mean
    casting the second argument to a long since the VarHandle excepts a
    long as well. (in the latest JDK you can use VarHandle::withInvokeExactBehavior to enforce that).

This could be made easier by wrapping the VarHandle set and get calls in helper methods that do the cast:

private static final VarHandle LONG_ARR_HANDLE 
        = MethodHandles.byteArrayViewVarHandle(long[].class,
                                               ByteOrder.nativeOrder());

public static void setLong(byte[] bytes, int index, long value) {
    LONG_ARR_HANDLE.set(bytes, index, value);
}   

public static long getLong(byte[] bytes, int index) {
    return (long) LONG_ARR_HANDLE.get(bytes, index);
}    

Answered By – Jorn Vernee

Answer Checked By – Mary Flores (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *