Add various/minor changes to some TProtocols for consistency and convenience
Clients: "java"
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
index 3407f62..bd36b6b 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
@@ -23,6 +23,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.UUID;
 import org.apache.thrift.TException;
+import org.apache.thrift.partial.TFieldData;
 import org.apache.thrift.transport.TTransport;
 import org.apache.thrift.transport.TTransportException;
 
@@ -536,29 +537,7 @@
       return TSTOP;
     }
 
-    short fieldId;
-
-    // mask off the 4 MSB of the type header. it could contain a field id delta.
-    short modifier = (short) ((type & 0xf0) >> 4);
-    if (modifier == 0) {
-      // not a delta. look ahead for the zigzag varint field id.
-      fieldId = readI16();
-    } else {
-      // has a delta. add the delta to the last read field id.
-      fieldId = (short) (lastFieldId_ + modifier);
-    }
-
-    TField field = new TField("", getTType((byte) (type & 0x0f)), fieldId);
-
-    // if this happens to be a boolean field, the value is encoded in the type
-    if (isBoolType(type)) {
-      // save the boolean value in a special instance variable.
-      boolValue_ = (byte) (type & 0x0f) == Types.BOOLEAN_TRUE ? Boolean.TRUE : Boolean.FALSE;
-    }
-
-    // push the new field onto the field stack so we can keep the deltas going.
-    lastFieldId_ = field.id;
-    return field;
+    return new TField("", getTType((byte) (type & 0x0f)), readFieldId(type));
   }
 
   /**
@@ -945,6 +924,43 @@
   // Additional methods to improve performance.
 
   @Override
+  public int readFieldBeginData() throws TException {
+    byte type = readByte();
+
+    // if it's a stop, then we can return immediately, as the struct is over.
+    if (type == TType.STOP) {
+      return TFieldData.encode(type);
+    }
+
+    return TFieldData.encode(getTType((byte) (type & 0x0f)), readFieldId(type));
+  }
+
+  // Only makes sense to be called by readFieldBegin and readFieldBeginData
+  private short readFieldId(byte type) throws TException {
+    short fieldId;
+
+    // mask off the 4 MSB of the type header. it could contain a field id delta.
+    short modifier = (short) ((type & 0xf0) >> 4);
+    if (modifier == 0) {
+      // not a delta. look ahead for the zigzag varint field id.
+      fieldId = readI16();
+    } else {
+      // has a delta. add the delta to the last read field id.
+      fieldId = (short) (lastFieldId_ + modifier);
+    }
+
+    // if this happens to be a boolean field, the value is encoded in the type
+    if (isBoolType(type)) {
+      // save the boolean value in a special instance variable.
+      boolValue_ = (byte) (type & 0x0f) == Types.BOOLEAN_TRUE ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    // push the new field onto the field stack so we can keep the deltas going.
+    lastFieldId_ = fieldId;
+    return fieldId;
+  }
+
+  @Override
   protected void skipBinary() throws TException {
     int size = intToZigZag(readI32());
     this.skipBytes(size);
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocol.java
index 22333d5..3fd27b4 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocol.java
@@ -223,7 +223,25 @@
    * @throws TException when any sub-operation fails
    */
   public final <K, V> Map<K, V> readMap(ReadMapEntryCallback<K, V> callback) throws TException {
-    return readMap(callback, HashMap::new);
+    return readMap(callback::getKey, callback::getValue);
+  }
+
+  /**
+   * read a {@link Map} of elements by delegating key reading to the callback, handles {@link
+   * #readMapBegin() begin} and {@link #readMapEnd() end} automatically. Calls to keyCallback and
+   * valueCallback will be in alternating orders, i.e. k1, v1, k2, v2, .., k_n, v_n
+   *
+   * @param keyCallback callback for reading keys
+   * @param valueCallback callback for reading values
+   * @param <K> key type
+   * @param <V> value type
+   * @return the map read
+   * @throws TException when any sub-operation fails
+   */
+  public final <K, V> Map<K, V> readMap(
+      ReadCollectionCallback<K> keyCallback, ReadCollectionCallback<V> valueCallback)
+      throws TException {
+    return readMap(keyCallback, valueCallback, size -> new HashMap<>(2 * size));
   }
 
   /**
@@ -242,11 +260,33 @@
    */
   public final <K, V> Map<K, V> readMap(
       ReadMapEntryCallback<K, V> callback, IntFunction<Map<K, V>> mapCreator) throws TException {
+    return readMap(callback::getKey, callback::getValue, mapCreator);
+  }
+
+  /**
+   * read a {@link Map} of elements by delegating key and value reading to the callback, handles
+   * {@link #readMapBegin() begin} and {@link #readMapEnd() end} automatically, with a specialized
+   * map creator given the size hint. Calls to keyCallback and valueCallback will be in alternating
+   * orders, i.e. k1, v1, k2, v2, .., k_n, v_n
+   *
+   * @param keyCallback callback for reading keys
+   * @param valueCallback callback for reading values
+   * @param mapCreator map creator given the size hint
+   * @param <K> key type
+   * @param <V> value type
+   * @return the map read
+   * @throws TException when any sub-operation fails
+   */
+  public final <K, V> Map<K, V> readMap(
+      ReadCollectionCallback<K> keyCallback,
+      ReadCollectionCallback<V> valueCallback,
+      IntFunction<Map<K, V>> mapCreator)
+      throws TException {
     return readMap(
         tMap -> {
           Map<K, V> map = mapCreator.apply(tMap.size);
           for (int i = 0; i < tMap.size; i += 1) {
-            map.put(callback.getKey(), callback.getValue());
+            map.put(keyCallback.call(), valueCallback.call());
           }
           return map;
         });
@@ -330,7 +370,7 @@
    * @throws TException when any sub-operation fails
    */
   public final <T> Set<T> readSet(ReadCollectionCallback<T> callback) throws TException {
-    return readSet(callback, HashSet::new);
+    return readSet(callback, size -> new HashSet<>(2 * size));
   }
 
   /**
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java
index bd9bd8e..f84ce81 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java
@@ -262,6 +262,46 @@
     return concreteProtocol.readBinary();
   }
 
+  @Override
+  public int readFieldBeginData() throws TException {
+    return concreteProtocol.readFieldBeginData();
+  }
+
+  @Override
+  protected void skipBool() throws TException {
+    concreteProtocol.skipBool();
+  }
+
+  @Override
+  protected void skipByte() throws TException {
+    concreteProtocol.skipByte();
+  }
+
+  @Override
+  protected void skipI16() throws TException {
+    concreteProtocol.skipI16();
+  }
+
+  @Override
+  protected void skipI32() throws TException {
+    concreteProtocol.skipI32();
+  }
+
+  @Override
+  protected void skipI64() throws TException {
+    concreteProtocol.skipI64();
+  }
+
+  @Override
+  protected void skipDouble() throws TException {
+    concreteProtocol.skipDouble();
+  }
+
+  @Override
+  protected void skipBinary() throws TException {
+    concreteProtocol.skipBinary();
+  }
+
   /**
    * @param type Returns the minimum amount of bytes needed to store the smallest possible instance
    *     of TType.
@@ -272,4 +312,9 @@
   public int getMinSerializedSize(byte type) throws TException {
     return concreteProtocol.getMinSerializedSize(type);
   }
+
+  @Override
+  public void reset() {
+    concreteProtocol.reset();
+  }
 }