DynamicCodec tweaks to make it into a super fast String serializer.

(3-8x faster than StringCodec).

PiperOrigin-RevId: 196615858
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/DynamicCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/DynamicCodec.java
index c4301a8..47e9a69 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/DynamicCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/DynamicCodec.java
@@ -14,7 +14,6 @@
 
 package com.google.devtools.build.lib.skyframe.serialization;
 
-import com.google.common.collect.ImmutableSortedMap;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.UnsafeProvider;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
@@ -26,6 +25,7 @@
 import java.nio.ByteBuffer;
 import java.util.Comparator;
 import java.util.Map;
+import java.util.TreeMap;
 import sun.reflect.ReflectionFactory;
 
 /**
@@ -37,7 +37,7 @@
 
   private final Class<?> type;
   private final Constructor<?> constructor;
-  private final ImmutableSortedMap<Field, Long> offsets;
+  private final TypeAndOffset[] offsets;
 
   public DynamicCodec(Class<?> type) throws ReflectiveOperationException {
     this.type = type;
@@ -58,8 +58,8 @@
   @Override
   public void serialize(SerializationContext context, Object obj, CodedOutputStream codedOut)
       throws SerializationException, IOException {
-    for (Map.Entry<Field, Long> entry : offsets.entrySet()) {
-      serializeField(context, codedOut, obj, entry.getKey().getType(), entry.getValue());
+    for (int i = 0; i < offsets.length; ++i) {
+      serializeField(context, codedOut, obj, offsets[i].type, offsets[i].offset);
     }
   }
 
@@ -105,6 +105,15 @@
       }
     } else if (type.isArray()) {
       Object arr = UnsafeProvider.getInstance().getObject(obj, offset);
+      if (type.getComponentType().equals(byte.class)) {
+        if (arr == null) {
+          codedOut.writeBoolNoTag(false);
+        } else {
+          codedOut.writeBoolNoTag(true);
+          codedOut.writeByteArrayNoTag((byte[]) arr);
+        }
+        return;
+      }
       if (arr == null) {
         codedOut.writeInt32NoTag(-1);
         return;
@@ -140,8 +149,8 @@
       throw new SerializationException("Could not instantiate object of type: " + type, e);
     }
     context.registerInitialValue(instance);
-    for (Map.Entry<Field, Long> entry : offsets.entrySet()) {
-      deserializeField(context, codedIn, instance, entry.getKey().getType(), entry.getValue());
+    for (int i = 0; i < offsets.length; ++i) {
+      deserializeField(context, codedIn, instance, offsets[i].type, offsets[i].offset);
     }
     return instance;
   }
@@ -185,6 +194,12 @@
         throw new UnsupportedOperationException("Unknown primitive type: " + type);
       }
     } else if (type.isArray()) {
+      if (type.getComponentType().equals(byte.class)) {
+        boolean isNonNull = codedIn.readBool();
+        UnsafeProvider.getInstance()
+            .putObject(obj, offset, isNonNull ? codedIn.readByteArray() : null);
+        return;
+      }
       int length = codedIn.readInt32();
       if (length < 0) {
         UnsafeProvider.getInstance().putObject(obj, offset, null);
@@ -206,9 +221,8 @@
     }
   }
 
-  private static <T> ImmutableSortedMap<Field, Long> getOffsets(Class<T> type) {
-    ImmutableSortedMap.Builder<Field, Long> offsets =
-        new ImmutableSortedMap.Builder<>(new FieldComparator());
+  private static <T> TypeAndOffset[] getOffsets(Class<T> type) {
+    TreeMap<Field, Long> offsets = new TreeMap<>(new FieldComparator());
     for (Class<? super T> next = type; next != null; next = next.getSuperclass()) {
       for (Field field : next.getDeclaredFields()) {
         if ((field.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) {
@@ -218,7 +232,24 @@
         offsets.put(field, UnsafeProvider.getInstance().objectFieldOffset(field));
       }
     }
-    return offsets.build();
+    // Converts to an array to make it easy to avoid the use of iterators.
+    TypeAndOffset[] offsetsArr = new TypeAndOffset[offsets.size()];
+    int i = 0;
+    for (Map.Entry<Field, Long> entry : offsets.entrySet()) {
+      offsetsArr[i] = new TypeAndOffset(entry.getKey().getType(), entry.getValue());
+      ++i;
+    }
+    return offsetsArr;
+  }
+
+  private static class TypeAndOffset {
+    public final Class<?> type;
+    public final long offset;
+
+    public TypeAndOffset(Class<?> type, long offset) {
+      this.type = type;
+      this.offset = offset;
+    }
   }
 
   private static Constructor<?> getConstructor(Class<?> type) throws ReflectiveOperationException {