| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.datasketches.memory.internal; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| |
| import sun.misc.Unsafe; |
| |
| /** |
| * Provides access to the sun.misc.Unsafe class and its key static fields. |
| * |
| * @author Lee Rhodes |
| */ |
| @SuppressWarnings({"restriction"}) |
| public final class UnsafeUtil { |
| public static final Unsafe unsafe; |
| public static final String JDK; //must be at least "1.8" |
| public static final int JDK_MAJOR; //8, 9, 10, 11, 12, etc |
| |
| //not an indicator of whether compressed references are used. |
| public static final int ADDRESS_SIZE; |
| |
| //For 64-bit JVMs: these offsets vary depending on coop: 16 for JVM <= 32GB; 24 for JVM > 32GB. |
| // Making this constant long-typed, rather than int, to exclude possibility of accidental overflow |
| // in expressions like arrayLength * ARRAY_BYTE_BASE_OFFSET, where arrayLength is int-typed. |
| // The same consideration for constants below: ARRAY_*_INDEX_SCALE, ARRAY_*_INDEX_SHIFT. |
| public static final long ARRAY_BOOLEAN_BASE_OFFSET; |
| public static final long ARRAY_BYTE_BASE_OFFSET; |
| public static final long ARRAY_SHORT_BASE_OFFSET; |
| public static final long ARRAY_CHAR_BASE_OFFSET; |
| public static final long ARRAY_INT_BASE_OFFSET; |
| public static final long ARRAY_LONG_BASE_OFFSET; |
| public static final long ARRAY_FLOAT_BASE_OFFSET; |
| public static final long ARRAY_DOUBLE_BASE_OFFSET; |
| public static final long ARRAY_OBJECT_BASE_OFFSET; |
| |
| //@formatter:off |
| |
| // Setting those values directly instead of using unsafe.arrayIndexScale(), because it may be |
| // beneficial for runtime execution, those values are backed into generated machine code as |
| // constants. E. g. see https://shipilev.net/jvm-anatomy-park/14-constant-variables/ |
| public static final int ARRAY_BOOLEAN_INDEX_SCALE = 1; |
| public static final int ARRAY_BYTE_INDEX_SCALE = 1; |
| public static final long ARRAY_SHORT_INDEX_SCALE = 2; |
| public static final long ARRAY_CHAR_INDEX_SCALE = 2; |
| public static final long ARRAY_INT_INDEX_SCALE = 4; |
| public static final long ARRAY_LONG_INDEX_SCALE = 8; |
| public static final long ARRAY_FLOAT_INDEX_SCALE = 4; |
| public static final long ARRAY_DOUBLE_INDEX_SCALE = 8; |
| public static final long ARRAY_OBJECT_INDEX_SCALE; // varies, 4 or 8 depending on coop |
| |
| //Used to convert "type" to bytes: bytes = longs << LONG_SHIFT |
| public static final int BOOLEAN_SHIFT = 0; |
| public static final int BYTE_SHIFT = 0; |
| public static final long SHORT_SHIFT = 1; |
| public static final long CHAR_SHIFT = 1; |
| public static final long INT_SHIFT = 2; |
| public static final long LONG_SHIFT = 3; |
| public static final long FLOAT_SHIFT = 2; |
| public static final long DOUBLE_SHIFT = 3; |
| public static final long OBJECT_SHIFT; // varies, 2 or 3 depending on coop |
| |
| public static final String LS = System.getProperty("line.separator"); |
| |
| //@formatter:on |
| |
| static { |
| try { |
| final Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor(); |
| unsafeConstructor.setAccessible(true); |
| unsafe = unsafeConstructor.newInstance(); |
| |
| // Alternative, but may not work across different JVMs. |
| // Field field = Unsafe.class.getDeclaredField("theUnsafe"); |
| // field.setAccessible(true); |
| // unsafe = (Unsafe) field.get(null); |
| |
| } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException |
| | InvocationTargetException | NoSuchMethodException e) { |
| e.printStackTrace(); |
| throw new RuntimeException("Unable to acquire Unsafe. " + e); |
| } |
| |
| //4 on 32-bit systems. 4 on 64-bit systems < 32GB, otherwise 8. |
| //This alone is not an indicator of compressed ref (coop) |
| ADDRESS_SIZE = unsafe.addressSize(); |
| |
| ARRAY_BOOLEAN_BASE_OFFSET = unsafe.arrayBaseOffset(boolean[].class); |
| ARRAY_BYTE_BASE_OFFSET = unsafe.arrayBaseOffset(byte[].class); |
| ARRAY_SHORT_BASE_OFFSET = unsafe.arrayBaseOffset(short[].class); |
| ARRAY_CHAR_BASE_OFFSET = unsafe.arrayBaseOffset(char[].class); |
| ARRAY_INT_BASE_OFFSET = unsafe.arrayBaseOffset(int[].class); |
| ARRAY_LONG_BASE_OFFSET = unsafe.arrayBaseOffset(long[].class); |
| ARRAY_FLOAT_BASE_OFFSET = unsafe.arrayBaseOffset(float[].class); |
| ARRAY_DOUBLE_BASE_OFFSET = unsafe.arrayBaseOffset(double[].class); |
| ARRAY_OBJECT_BASE_OFFSET = unsafe.arrayBaseOffset(Object[].class); |
| |
| ARRAY_OBJECT_INDEX_SCALE = unsafe.arrayIndexScale(Object[].class); |
| OBJECT_SHIFT = ARRAY_OBJECT_INDEX_SCALE == 4 ? 2 : 3; |
| |
| final String jdkVer = System.getProperty("java.version"); |
| final int[] p = parseJavaVersion(jdkVer); |
| JDK = p[0] + "." + p[1]; |
| JDK_MAJOR = (p[0] == 1) ? p[1] : p[0]; |
| } |
| |
| private UnsafeUtil() {} |
| |
| /** |
| * Returns first two number groups of the java version string. |
| * @param jdkVer the java version string from System.getProperty("java.version"). |
| * @return first two number groups of the java version string. |
| */ |
| public static int[] parseJavaVersion(final String jdkVer) { |
| final int p0, p1; |
| try { |
| String[] parts = jdkVer.trim().split("[^0-9\\.]");//grab only number groups and "." |
| parts = parts[0].split("\\."); //split out the number groups |
| p0 = Integer.parseInt(parts[0]); //the first number group |
| p1 = (parts.length > 1) ? Integer.parseInt(parts[1]) : 0; //2nd number group, or 0 |
| } catch (final NumberFormatException | ArrayIndexOutOfBoundsException e) { |
| throw new IllegalArgumentException("Improper Java -version string: " + jdkVer + "\n" + e); |
| } |
| checkJavaVersion(jdkVer, p0, p1); |
| return new int[] {p0, p1}; |
| } |
| |
| public static void checkJavaVersion(final String jdkVer, final int p0, final int p1) { |
| if ( (p0 < 1) || ((p0 == 1) && (p1 < 8)) || (p0 > 13) ) { |
| throw new IllegalArgumentException( |
| "Unsupported JDK Major Version, must be one of 1.8, 8, 11, 17: " + jdkVer); |
| } |
| } |
| |
| public static long getFieldOffset(final Class<?> c, final String fieldName) { |
| try { |
| return unsafe.objectFieldOffset(c.getDeclaredField(fieldName)); |
| } catch (final NoSuchFieldException e) { |
| throw new IllegalStateException(e + ": " + fieldName); |
| } |
| } |
| |
| /** |
| * Like {@link Unsafe#arrayBaseOffset(Class)}, but caches return values for common array types. |
| * Useful because calling {@link Unsafe#arrayBaseOffset(Class)} directly incurs more overhead. |
| * @param c The given Class<?>. |
| * @return the base-offset |
| */ |
| public static long getArrayBaseOffset(final Class<?> c) { |
| // Ordering here is roughly in order of what we expect to be most popular. |
| if (c == byte[].class) { |
| return ARRAY_BYTE_BASE_OFFSET; |
| } else if (c == int[].class) { |
| return ARRAY_INT_BASE_OFFSET; |
| } else if (c == long[].class) { |
| return ARRAY_LONG_BASE_OFFSET; |
| } else if (c == float[].class) { |
| return ARRAY_FLOAT_BASE_OFFSET; |
| } else if (c == double[].class) { |
| return ARRAY_DOUBLE_BASE_OFFSET; |
| } else if (c == boolean[].class) { |
| return ARRAY_BOOLEAN_BASE_OFFSET; |
| } else if (c == short[].class) { |
| return ARRAY_SHORT_BASE_OFFSET; |
| } else if (c == char[].class) { |
| return ARRAY_CHAR_BASE_OFFSET; |
| } else if (c == Object[].class) { |
| return ARRAY_OBJECT_BASE_OFFSET; |
| } else { |
| return unsafe.arrayBaseOffset(c); |
| } |
| } |
| |
| } |