Unify MessageSchema.writeFieldsInAscendingOrder proto2 and proto3 methods.

This unifies proto2 and proto3 (and later editions) implementations to incorporate performance optimizations directly referencing unsafe and caching reflection presence field (and offset) to all syntaxes. These optimizations were originally added in cl/187404278 for proto2 only.

These optimizations do not seem to be meaningfully proto2/3-specific. The UnsafeUtil methods simply wrap the corresponding Unsafe methods. Presence bit fields are used in the same way for proto3, but this optimization seems to predate proto3 optional.

While in there, better document how the buffer encoding and mask/offsets work.

PiperOrigin-RevId: 540320373
pull/13060/head
Sandy Zhang 1 year ago committed by Copybara-Service
parent 5a48187464
commit d5067ccbe6
  1. 550
      java/core/src/main/java/com/google/protobuf/MessageSchema.java

@ -121,22 +121,31 @@ final class MessageSchema<T> implements Schema<T> {
* <p>
*
* <pre>
* buffer[i]
* [ 0 - 3] unused
* [ 4 - 31] field number
*
* buffer[i+1]
* [32 - 33] unused
* [34 - 34] whether UTF-8 enforcement should be applied to a string field.
* [35 - 35] whether the field is required
* [36 - 43] field type / for oneof: field type + {@link #ONEOF_TYPE_OFFSET}
* [44 - 63] field offset / oneof value field offset
*
* buffer[i+2]
* [64 - 69] unused
* [70 - 75] field presence mask shift (unused for oneof/repeated fields)
* [76 - 95] presence field offset / oneof case field offset / cached size field offset
* </pre>
*
* Note that presence field offset can only use 20 bits - 1. All bits set to 1 is the sentinel
* value for non-presence. This is not validated at runtime, we simply assume message layouts
* will not exceed 1MB (assuming ~10 bytes per field, that implies 100k fields which should hit
* other javac limits first).
* Offset refer to the field offsets returned by Unsafe.objectFieldOffset() for reflective access
* to corresponding field.
*
* <p>Note that presence field offset can only use 20 bits - 1. All bits set to 1 is the sentinel
* value for non-presence. This is not validated at runtime, we simply assume message layouts will
* not exceed 1MB (assuming ~10 bytes per field, that implies 100k fields which should hit other
* javac limits first). This corresponds to a shared bitFieldN_, which must have the field
* presence mask shift applied to get the corresponding field's presence.
*/
private final int[] buffer;
@ -2600,19 +2609,12 @@ final class MessageSchema<T> implements Schema<T> {
if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) {
writeFieldsInDescendingOrder(message, writer);
} else {
switch (syntax) {
case PROTO3:
writeFieldsInAscendingOrderProto3(message, writer);
break;
case PROTO2:
writeFieldsInAscendingOrderProto2(message, writer);
break;
}
writeFieldsInAscendingOrder(message, writer);
}
}
@SuppressWarnings("unchecked")
private void writeFieldsInAscendingOrderProto2(T message, Writer writer) throws IOException {
private void writeFieldsInAscendingOrder(T message, Writer writer) throws IOException {
Iterator<? extends Map.Entry<?, ?>> extensionIterator = null;
Map.Entry nextExtension = null;
if (hasExtensions) {
@ -2622,6 +2624,7 @@ final class MessageSchema<T> implements Schema<T> {
nextExtension = extensionIterator.next();
}
}
int currentPresenceFieldOffset = NO_PRESENCE_SENTINEL;
int currentPresenceField = 0;
final int bufferLength = buffer.length;
@ -2631,15 +2634,23 @@ final class MessageSchema<T> implements Schema<T> {
final int number = numberAt(pos);
final int fieldType = type(typeAndOffset);
int presenceMaskAndOffset = 0;
int presenceMask = 0;
if (fieldType <= 17) {
presenceMaskAndOffset = buffer[pos + 2];
int presenceMaskAndOffset = buffer[pos + 2];
final int presenceFieldOffset = presenceMaskAndOffset & OFFSET_MASK;
// Performance optimization to cache the presence field which is shared for multiple
// fields.
// TODO(b/279034699): Improve caching for case when fields alternate between having and not
// having presence by caching presence field for last field with presence only.
if (presenceFieldOffset != currentPresenceFieldOffset) {
currentPresenceFieldOffset = presenceFieldOffset;
currentPresenceField = unsafe.getInt(message, (long) presenceFieldOffset);
currentPresenceField =
currentPresenceFieldOffset == NO_PRESENCE_SENTINEL
? 0
: unsafe.getInt(message, (long) presenceFieldOffset);
}
// Mask for presence bit of the current field from the shared presence field.
presenceMask = 1 << (presenceMaskAndOffset >>> OFFSET_BITS);
}
@ -2652,93 +2663,111 @@ final class MessageSchema<T> implements Schema<T> {
switch (fieldType) {
case 0: // DOUBLE:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeDouble(number, doubleAt(message, offset));
}
break;
case 1: // FLOAT:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeFloat(number, floatAt(message, offset));
}
break;
case 2: // INT64:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeInt64(number, unsafe.getLong(message, offset));
}
break;
case 3: // UINT64:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeUInt64(number, unsafe.getLong(message, offset));
}
break;
case 4: // INT32:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeInt32(number, unsafe.getInt(message, offset));
}
break;
case 5: // FIXED64:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeFixed64(number, unsafe.getLong(message, offset));
}
break;
case 6: // FIXED32:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeFixed32(number, unsafe.getInt(message, offset));
}
break;
case 7: // BOOL:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeBool(number, booleanAt(message, offset));
}
break;
case 8: // STRING:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writeString(number, unsafe.getObject(message, offset), writer);
}
break;
case 9: // MESSAGE:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
Object value = unsafe.getObject(message, offset);
writer.writeMessage(number, value, getMessageFieldSchema(pos));
}
break;
case 10: // BYTES:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeBytes(number, (ByteString) unsafe.getObject(message, offset));
}
break;
case 11: // UINT32:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeUInt32(number, unsafe.getInt(message, offset));
}
break;
case 12: // ENUM:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeEnum(number, unsafe.getInt(message, offset));
}
break;
case 13: // SFIXED32:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeSFixed32(number, unsafe.getInt(message, offset));
}
break;
case 14: // SFIXED64:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeSFixed64(number, unsafe.getLong(message, offset));
}
break;
case 15: // SINT32:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeSInt32(number, unsafe.getInt(message, offset));
}
break;
case 16: // SINT64:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeSInt64(number, unsafe.getLong(message, offset));
}
break;
case 17: // GROUP:
if ((currentPresenceField & presenceMask) != 0) {
if (isFieldPresent(
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
writer.writeGroup(
number, unsafe.getObject(message, offset), getMessageFieldSchema(pos));
}
@ -2987,459 +3016,6 @@ final class MessageSchema<T> implements Schema<T> {
writeUnknownInMessageTo(unknownFieldSchema, message, writer);
}
@SuppressWarnings("unchecked")
private void writeFieldsInAscendingOrderProto3(T message, Writer writer) throws IOException {
Iterator<? extends Map.Entry<?, ?>> extensionIterator = null;
Map.Entry nextExtension = null;
if (hasExtensions) {
FieldSet<?> extensions = extensionSchema.getExtensions(message);
if (!extensions.isEmpty()) {
extensionIterator = extensions.iterator();
nextExtension = extensionIterator.next();
}
}
final int bufferLength = buffer.length;
for (int pos = 0; pos < bufferLength; pos += INTS_PER_FIELD) {
final int typeAndOffset = typeAndOffsetAt(pos);
final int number = numberAt(pos);
// Write any extensions that need to be written before the current field.
while (nextExtension != null && extensionSchema.extensionNumber(nextExtension) <= number) {
extensionSchema.serializeExtension(writer, nextExtension);
nextExtension = extensionIterator.hasNext() ? extensionIterator.next() : null;
}
switch (type(typeAndOffset)) {
case 0: // DOUBLE:
if (isFieldPresent(message, pos)) {
writer.writeDouble(number, doubleAt(message, offset(typeAndOffset)));
}
break;
case 1: // FLOAT:
if (isFieldPresent(message, pos)) {
writer.writeFloat(number, floatAt(message, offset(typeAndOffset)));
}
break;
case 2: // INT64:
if (isFieldPresent(message, pos)) {
writer.writeInt64(number, longAt(message, offset(typeAndOffset)));
}
break;
case 3: // UINT64:
if (isFieldPresent(message, pos)) {
writer.writeUInt64(number, longAt(message, offset(typeAndOffset)));
}
break;
case 4: // INT32:
if (isFieldPresent(message, pos)) {
writer.writeInt32(number, intAt(message, offset(typeAndOffset)));
}
break;
case 5: // FIXED64:
if (isFieldPresent(message, pos)) {
writer.writeFixed64(number, longAt(message, offset(typeAndOffset)));
}
break;
case 6: // FIXED32:
if (isFieldPresent(message, pos)) {
writer.writeFixed32(number, intAt(message, offset(typeAndOffset)));
}
break;
case 7: // BOOL:
if (isFieldPresent(message, pos)) {
writer.writeBool(number, booleanAt(message, offset(typeAndOffset)));
}
break;
case 8: // STRING:
if (isFieldPresent(message, pos)) {
writeString(number, UnsafeUtil.getObject(message, offset(typeAndOffset)), writer);
}
break;
case 9: // MESSAGE:
if (isFieldPresent(message, pos)) {
Object value = UnsafeUtil.getObject(message, offset(typeAndOffset));
writer.writeMessage(number, value, getMessageFieldSchema(pos));
}
break;
case 10: // BYTES:
if (isFieldPresent(message, pos)) {
writer.writeBytes(
number, (ByteString) UnsafeUtil.getObject(message, offset(typeAndOffset)));
}
break;
case 11: // UINT32:
if (isFieldPresent(message, pos)) {
writer.writeUInt32(number, intAt(message, offset(typeAndOffset)));
}
break;
case 12: // ENUM:
if (isFieldPresent(message, pos)) {
writer.writeEnum(number, intAt(message, offset(typeAndOffset)));
}
break;
case 13: // SFIXED32:
if (isFieldPresent(message, pos)) {
writer.writeSFixed32(number, intAt(message, offset(typeAndOffset)));
}
break;
case 14: // SFIXED64:
if (isFieldPresent(message, pos)) {
writer.writeSFixed64(number, longAt(message, offset(typeAndOffset)));
}
break;
case 15: // SINT32:
if (isFieldPresent(message, pos)) {
writer.writeSInt32(number, intAt(message, offset(typeAndOffset)));
}
break;
case 16: // SINT64:
if (isFieldPresent(message, pos)) {
writer.writeSInt64(number, longAt(message, offset(typeAndOffset)));
}
break;
case 17: // GROUP:
if (isFieldPresent(message, pos)) {
writer.writeGroup(
number,
UnsafeUtil.getObject(message, offset(typeAndOffset)),
getMessageFieldSchema(pos));
}
break;
case 18: // DOUBLE_LIST:
SchemaUtil.writeDoubleList(
numberAt(pos),
(List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 19: // FLOAT_LIST:
SchemaUtil.writeFloatList(
numberAt(pos),
(List<Float>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 20: // INT64_LIST:
SchemaUtil.writeInt64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 21: // UINT64_LIST:
SchemaUtil.writeUInt64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 22: // INT32_LIST:
SchemaUtil.writeInt32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 23: // FIXED64_LIST:
SchemaUtil.writeFixed64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 24: // FIXED32_LIST:
SchemaUtil.writeFixed32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 25: // BOOL_LIST:
SchemaUtil.writeBoolList(
numberAt(pos),
(List<Boolean>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 26: // STRING_LIST:
SchemaUtil.writeStringList(
numberAt(pos),
(List<String>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer);
break;
case 27: // MESSAGE_LIST:
SchemaUtil.writeMessageList(
numberAt(pos),
(List<?>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
getMessageFieldSchema(pos));
break;
case 28: // BYTES_LIST:
SchemaUtil.writeBytesList(
numberAt(pos),
(List<ByteString>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer);
break;
case 29: // UINT32_LIST:
SchemaUtil.writeUInt32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 30: // ENUM_LIST:
SchemaUtil.writeEnumList(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 31: // SFIXED32_LIST:
SchemaUtil.writeSFixed32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 32: // SFIXED64_LIST:
SchemaUtil.writeSFixed64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 33: // SINT32_LIST:
SchemaUtil.writeSInt32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 34: // SINT64_LIST:
SchemaUtil.writeSInt64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
false);
break;
case 35: // DOUBLE_LIST_PACKED:
// TODO(xiaofeng): Make use of cached field size to speed up serialization.
SchemaUtil.writeDoubleList(
numberAt(pos),
(List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 36: // FLOAT_LIST_PACKED:
SchemaUtil.writeFloatList(
numberAt(pos),
(List<Float>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 37: // INT64_LIST_PACKED:
SchemaUtil.writeInt64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 38: // UINT64_LIST_PACKED:
SchemaUtil.writeUInt64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 39: // INT32_LIST_PACKED:
SchemaUtil.writeInt32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 40: // FIXED64_LIST_PACKED:
SchemaUtil.writeFixed64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 41: // FIXED32_LIST_PACKED:
SchemaUtil.writeFixed32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 42: // BOOL_LIST_PACKED:
SchemaUtil.writeBoolList(
numberAt(pos),
(List<Boolean>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 43: // UINT32_LIST_PACKED:
SchemaUtil.writeUInt32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 44: // ENUM_LIST_PACKED:
SchemaUtil.writeEnumList(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 45: // SFIXED32_LIST_PACKED:
SchemaUtil.writeSFixed32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 46: // SFIXED64_LIST_PACKED:
SchemaUtil.writeSFixed64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 47: // SINT32_LIST_PACKED:
SchemaUtil.writeSInt32List(
numberAt(pos),
(List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 48: // SINT64_LIST_PACKED:
SchemaUtil.writeSInt64List(
numberAt(pos),
(List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
true);
break;
case 49: // GROUP_LIST:
SchemaUtil.writeGroupList(
numberAt(pos),
(List<?>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
writer,
getMessageFieldSchema(pos));
break;
case 50: // MAP:
// TODO(dweis): Use schema cache.
writeMapHelper(writer, number, UnsafeUtil.getObject(message, offset(typeAndOffset)), pos);
break;
case 51: // ONEOF_DOUBLE:
if (isOneofPresent(message, number, pos)) {
writer.writeDouble(number, oneofDoubleAt(message, offset(typeAndOffset)));
}
break;
case 52: // ONEOF_FLOAT:
if (isOneofPresent(message, number, pos)) {
writer.writeFloat(number, oneofFloatAt(message, offset(typeAndOffset)));
}
break;
case 53: // ONEOF_INT64:
if (isOneofPresent(message, number, pos)) {
writer.writeInt64(number, oneofLongAt(message, offset(typeAndOffset)));
}
break;
case 54: // ONEOF_UINT64:
if (isOneofPresent(message, number, pos)) {
writer.writeUInt64(number, oneofLongAt(message, offset(typeAndOffset)));
}
break;
case 55: // ONEOF_INT32:
if (isOneofPresent(message, number, pos)) {
writer.writeInt32(number, oneofIntAt(message, offset(typeAndOffset)));
}
break;
case 56: // ONEOF_FIXED64:
if (isOneofPresent(message, number, pos)) {
writer.writeFixed64(number, oneofLongAt(message, offset(typeAndOffset)));
}
break;
case 57: // ONEOF_FIXED32:
if (isOneofPresent(message, number, pos)) {
writer.writeFixed32(number, oneofIntAt(message, offset(typeAndOffset)));
}
break;
case 58: // ONEOF_BOOL:
if (isOneofPresent(message, number, pos)) {
writer.writeBool(number, oneofBooleanAt(message, offset(typeAndOffset)));
}
break;
case 59: // ONEOF_STRING:
if (isOneofPresent(message, number, pos)) {
writeString(number, UnsafeUtil.getObject(message, offset(typeAndOffset)), writer);
}
break;
case 60: // ONEOF_MESSAGE:
if (isOneofPresent(message, number, pos)) {
Object value = UnsafeUtil.getObject(message, offset(typeAndOffset));
writer.writeMessage(number, value, getMessageFieldSchema(pos));
}
break;
case 61: // ONEOF_BYTES:
if (isOneofPresent(message, number, pos)) {
writer.writeBytes(
number, (ByteString) UnsafeUtil.getObject(message, offset(typeAndOffset)));
}
break;
case 62: // ONEOF_UINT32:
if (isOneofPresent(message, number, pos)) {
writer.writeUInt32(number, oneofIntAt(message, offset(typeAndOffset)));
}
break;
case 63: // ONEOF_ENUM:
if (isOneofPresent(message, number, pos)) {
writer.writeEnum(number, oneofIntAt(message, offset(typeAndOffset)));
}
break;
case 64: // ONEOF_SFIXED32:
if (isOneofPresent(message, number, pos)) {
writer.writeSFixed32(number, oneofIntAt(message, offset(typeAndOffset)));
}
break;
case 65: // ONEOF_SFIXED64:
if (isOneofPresent(message, number, pos)) {
writer.writeSFixed64(number, oneofLongAt(message, offset(typeAndOffset)));
}
break;
case 66: // ONEOF_SINT32:
if (isOneofPresent(message, number, pos)) {
writer.writeSInt32(number, oneofIntAt(message, offset(typeAndOffset)));
}
break;
case 67: // ONEOF_SINT64:
if (isOneofPresent(message, number, pos)) {
writer.writeSInt64(number, oneofLongAt(message, offset(typeAndOffset)));
}
break;
case 68: // ONEOF_GROUP:
if (isOneofPresent(message, number, pos)) {
writer.writeGroup(
number,
UnsafeUtil.getObject(message, offset(typeAndOffset)),
getMessageFieldSchema(pos));
}
break;
default:
// Assume it's an empty entry - just go to the next entry.
break;
}
}
while (nextExtension != null) {
extensionSchema.serializeExtension(writer, nextExtension);
nextExtension = extensionIterator.hasNext() ? extensionIterator.next() : null;
}
writeUnknownInMessageTo(unknownFieldSchema, message, writer);
}
@SuppressWarnings("unchecked")
private void writeFieldsInDescendingOrder(T message, Writer writer) throws IOException {
writeUnknownInMessageTo(unknownFieldSchema, message, writer);

Loading…
Cancel
Save