Merge pull request #109 from gianm/cache-array-base-offset

Cache arrayBaseOffset.
diff --git a/src/main/java/org/apache/datasketches/memory/BaseState.java b/src/main/java/org/apache/datasketches/memory/BaseState.java
index 8d9e12d..f8a7cf9 100644
--- a/src/main/java/org/apache/datasketches/memory/BaseState.java
+++ b/src/main/java/org/apache/datasketches/memory/BaseState.java
@@ -94,7 +94,7 @@
     capacityBytes_ = capacityBytes;
     cumBaseOffset_ = regionOffset + ((unsafeObj == null)
         ? nativeBaseOffset
-        : unsafe.arrayBaseOffset(unsafeObj.getClass()));
+        : UnsafeUtil.getArrayBaseOffset(unsafeObj.getClass()));
   }
 
   //Byte Order Related
@@ -233,7 +233,7 @@
     final Object unsafeObj = getUnsafeObject();
     return (unsafeObj == null)
         ? cumBaseOffset_ - getNativeBaseOffset()
-        : cumBaseOffset_ - unsafe.arrayBaseOffset(unsafeObj.getClass());
+        : cumBaseOffset_ - UnsafeUtil.getArrayBaseOffset(unsafeObj.getClass());
   }
 
   /**
@@ -518,7 +518,7 @@
       uObjHeader = 0;
     } else {
       uObjStr =  uObj.getClass().getSimpleName() + ", " + (uObj.hashCode() & 0XFFFFFFFFL);
-      uObjHeader = unsafe.arrayBaseOffset(uObj.getClass());
+      uObjHeader = UnsafeUtil.getArrayBaseOffset(uObj.getClass());
     }
     final ByteBuffer bb = state.getByteBuffer();
     final String bbStr = (bb == null) ? "null"
diff --git a/src/main/java/org/apache/datasketches/memory/UnsafeUtil.java b/src/main/java/org/apache/datasketches/memory/UnsafeUtil.java
index 7d47580..7349829 100644
--- a/src/main/java/org/apache/datasketches/memory/UnsafeUtil.java
+++ b/src/main/java/org/apache/datasketches/memory/UnsafeUtil.java
@@ -158,6 +158,35 @@
   }
 
   /**
+   * Like {@link Unsafe#arrayBaseOffset(Class)}, but caches return values for common array types. Useful because
+   * calling {@link Unsafe#arrayBaseOffset(Class)} directly incurs more overhead.
+   */
+  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);
+    }
+  }
+
+  /**
    * Assert the requested offset and length against the allocated size.
    * The invariants equation is: {@code 0 <= reqOff <= reqLen <= reqOff + reqLen <= allocSize}.
    * If this equation is violated and assertions are enabled, an {@link AssertionError} will
diff --git a/src/test/java/org/apache/datasketches/memory/UnsafeUtilTest.java b/src/test/java/org/apache/datasketches/memory/UnsafeUtilTest.java
index 2afa8e3..3c7ec09 100644
--- a/src/test/java/org/apache/datasketches/memory/UnsafeUtilTest.java
+++ b/src/test/java/org/apache/datasketches/memory/UnsafeUtilTest.java
@@ -23,6 +23,9 @@
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.testng.annotations.Test;
 
 
@@ -123,6 +126,30 @@
   }
 
   @Test
+  public void checkArrayBaseOffset()
+  {
+    final List<Class<?>> classes = new ArrayList<>();
+    classes.add(byte[].class);
+    classes.add(int[].class);
+    classes.add(long[].class);
+    classes.add(float[].class);
+    classes.add(double[].class);
+    classes.add(boolean[].class);
+    classes.add(short[].class);
+    classes.add(char[].class);
+    classes.add(Object[].class);
+    classes.add(byte[][].class); // An array type that is not cached
+
+    for (Class<?> clazz : classes) {
+      assertEquals(
+          UnsafeUtil.getArrayBaseOffset(clazz),
+          UnsafeUtil.unsafe.arrayBaseOffset(clazz),
+          clazz.getTypeName()
+      );
+    }
+  }
+
+  @Test
   public void printlnTest() {
     println("PRINTING: "+this.getClass().getName());
   }