aboutsummaryrefslogtreecommitdiff
path: root/java/core/src/main/java/com/google/protobuf/SchemaUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/core/src/main/java/com/google/protobuf/SchemaUtil.java')
-rw-r--r--java/core/src/main/java/com/google/protobuf/SchemaUtil.java516
1 files changed, 516 insertions, 0 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/SchemaUtil.java b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java
new file mode 100644
index 00000000..db2dc949
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java
@@ -0,0 +1,516 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Internal.ProtobufList;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Helper methods used by schemas. */
+@ExperimentalApi
+public final class SchemaUtil {
+ private static final Class<?> UNMODIFIABLE_LIST_CLASS =
+ Collections.unmodifiableList(new ArrayList<Integer>()).getClass();
+ private static final Class<?> ABSTRACT_MESSAGE_CLASS = getAbstractMessageClass();
+ private static final Class<?> GENERATED_MESSAGE_CLASS = getGeneratedMessageClass();
+ private static final Class<?> UNKNOWN_FIELD_SET_CLASS = getUnknownFieldSetClass();
+ private static final long UNKNOWN_FIELD_OFFSET = unknownFieldOffset();
+ private static final long LITE_UNKNOWN_FIELD_OFFSET = unknownFieldLiteOffset();
+ private static final Object DEFAULT_UNKNOWN_FIELD_SET = defaultUnknownFieldSet();
+ private static final long MEMOIZED_SIZE_FIELD_OFFSET = getMemoizedSizeFieldOffset();
+ private static final long LITE_MEMOIZED_SIZE_FIELD_OFFSET = getLiteMemoizedSizeFieldOffset();
+
+ private SchemaUtil() {}
+
+ /**
+ * Initializes all of the base class fields for the given {@link GeneratedMessageLite} to their
+ * default values.
+ */
+ public static void initLiteBaseClassFields(Object msg) {
+ UnsafeUtil.putObject(msg, LITE_UNKNOWN_FIELD_OFFSET, UnknownFieldSetLite.getDefaultInstance());
+ UnsafeUtil.putInt(msg, LITE_MEMOIZED_SIZE_FIELD_OFFSET, -1);
+ }
+
+ /**
+ * Initializes all of the base class fields for the given {@link
+ * com.google.protobuf.GeneratedMessage} to their default values.
+ */
+ public static void initBaseClassFields(Object msg) {
+ UnsafeUtil.putObject(msg, UNKNOWN_FIELD_OFFSET, DEFAULT_UNKNOWN_FIELD_SET);
+ UnsafeUtil.putInt(msg, MEMOIZED_SIZE_FIELD_OFFSET, -1);
+ }
+
+ /**
+ * Requires that the given message extend {@link com.google.protobuf.GeneratedMessage} or {@link
+ * GeneratedMessageLite}.
+ */
+ public static void requireGeneratedMessage(Class<?> messageType) {
+ if (!GeneratedMessageLite.class.isAssignableFrom(messageType)
+ && !GENERATED_MESSAGE_CLASS.isAssignableFrom(messageType)) {
+ throw new IllegalArgumentException(
+ "Message classes must extend GeneratedMessage or GeneratedMessageLite");
+ }
+ }
+
+ /** Initializes the message's memoized size to the default value. */
+ public static void initMemoizedSize(Object msg) {
+ final long fieldOffset =
+ (msg instanceof GeneratedMessageLite)
+ ? LITE_MEMOIZED_SIZE_FIELD_OFFSET
+ : MEMOIZED_SIZE_FIELD_OFFSET;
+
+ if (fieldOffset < 0) {
+ throw new IllegalArgumentException(
+ "Unable to identify memoizedSize field offset for message of type: "
+ + msg.getClass().getName());
+ }
+
+ UnsafeUtil.putInt(msg, fieldOffset, -1);
+ }
+
+ public static void writeDouble(int fieldNumber, double value, Writer writer) {
+ if (Double.compare(value, 0.0) != 0) {
+ writer.writeDouble(fieldNumber, value);
+ }
+ }
+
+ public static void writeFloat(int fieldNumber, float value, Writer writer) {
+ if (Float.compare(value, 0.0f) != 0) {
+ writer.writeFloat(fieldNumber, value);
+ }
+ }
+
+ public static void writeInt64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeInt64(fieldNumber, value);
+ }
+ }
+
+ public static void writeUInt64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeUInt64(fieldNumber, value);
+ }
+ }
+
+ public static void writeSInt64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeSInt64(fieldNumber, value);
+ }
+ }
+
+ public static void writeFixed64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeFixed64(fieldNumber, value);
+ }
+ }
+
+ public static void writeSFixed64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeSFixed64(fieldNumber, value);
+ }
+ }
+
+ public static void writeInt32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeInt32(fieldNumber, value);
+ }
+ }
+
+ public static void writeUInt32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeUInt32(fieldNumber, value);
+ }
+ }
+
+ public static void writeSInt32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeSInt32(fieldNumber, value);
+ }
+ }
+
+ public static void writeFixed32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeFixed32(fieldNumber, value);
+ }
+ }
+
+ public static void writeSFixed32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeSFixed32(fieldNumber, value);
+ }
+ }
+
+ public static void writeEnum(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeEnum(fieldNumber, value);
+ }
+ }
+
+ public static void writeBool(int fieldNumber, boolean value, Writer writer) {
+ if (value) {
+ writer.writeBool(fieldNumber, true);
+ }
+ }
+
+ public static void writeString(int fieldNumber, Object value, Writer writer) {
+ if (value instanceof String) {
+ writeStringInternal(fieldNumber, (String) value, writer);
+ } else {
+ writeBytes(fieldNumber, (ByteString) value, writer);
+ }
+ }
+
+ private static void writeStringInternal(int fieldNumber, String value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeString(fieldNumber, value);
+ }
+ }
+
+ public static void writeBytes(int fieldNumber, ByteString value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeBytes(fieldNumber, value);
+ }
+ }
+
+ public static void writeMessage(int fieldNumber, Object value, Writer writer) {
+ if (value != null) {
+ writer.writeMessage(fieldNumber, value);
+ }
+ }
+
+ public static void writeDoubleList(
+ int fieldNumber, List<Double> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeDoubleList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeFloatList(
+ int fieldNumber, List<Float> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeFloatList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeInt64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeInt64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeUInt64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeUInt64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSInt64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSInt64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeFixed64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeFixed64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSFixed64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSFixed64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeInt32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeInt32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeUInt32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeUInt32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSInt32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSInt32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeFixed32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeFixed32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSFixed32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSFixed32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeEnumList(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeEnumList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeBoolList(
+ int fieldNumber, List<Boolean> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeBoolList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeStringList(int fieldNumber, List<String> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeStringList(fieldNumber, value);
+ }
+ }
+
+ public static void writeBytesList(int fieldNumber, List<ByteString> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeBytesList(fieldNumber, value);
+ }
+ }
+
+ public static void writeMessageList(int fieldNumber, List<?> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeMessageList(fieldNumber, value);
+ }
+ }
+
+ public static void writeGroupList(int fieldNumber, List<?> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeGroupList(fieldNumber, value);
+ }
+ }
+
+ /**
+ * Used for protobuf-lite (i.e. messages that extend {@link GeneratedMessageLite}). Reads in a
+ * list of messages into a target {@link ProtobufList} field in the message.
+ */
+ public static final <T> void readProtobufMessageList(
+ Object message, long offset, Reader reader, Class<T> targetType) throws IOException {
+ reader.readMessageList(SchemaUtil.<T>mutableProtobufListAt(message, offset), targetType);
+ }
+
+ /**
+ * Used for standard protobuf messages (i.e. messages that extend {@link
+ * com.google.protobuf.GeneratedMessage}). Reads in a list of messages into a target list field in
+ * the message.
+ */
+ public static <T> void readMessageList(
+ Object message, long offset, Reader reader, Class<T> targetType) throws IOException {
+ reader.readMessageList(SchemaUtil.<T>mutableListAt(message, offset), targetType);
+ }
+
+ /**
+ * Used for group types. Reads in a list of group messages into a target list field in the
+ * message.
+ */
+ public static <T> void readGroupList(
+ Object message, long offset, Reader reader, Class<T> targetType) throws IOException {
+ reader.readGroupList(SchemaUtil.<T>mutableListAt(message, offset), targetType);
+ }
+
+ /**
+ * Used for protobuf-lite (i.e. messages that extend GeneratedMessageLite). Converts the {@link
+ * ProtobufList} at the given field position in the message into a mutable list (if it isn't
+ * already).
+ */
+ @SuppressWarnings("unchecked")
+ public static <L> ProtobufList<L> mutableProtobufListAt(Object message, long pos) {
+ ProtobufList<L> list = (ProtobufList<L>) UnsafeUtil.getObject(message, pos);
+ if (!list.isModifiable()) {
+ int size = list.size();
+ list = list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ UnsafeUtil.putObject(message, pos, list);
+ }
+ return list;
+ }
+
+ /** Converts the list at the given field location in the message into a mutable list. */
+ @SuppressWarnings("unchecked")
+ public static <L> List<L> mutableListAt(Object message, long pos) {
+ List<L> list = (List<L>) UnsafeUtil.getObject(message, pos);
+ if (list.isEmpty()) {
+ list =
+ (list instanceof LazyStringList)
+ ? (List<L>) new LazyStringArrayList()
+ : new ArrayList<L>();
+ UnsafeUtil.putObject(message, pos, list);
+ } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) {
+ list = new ArrayList<L>(list);
+ UnsafeUtil.putObject(message, pos, list);
+ } else if (list instanceof UnmodifiableLazyStringList) {
+ // Convert the list to a mutable list.
+ list = (List<L>) new LazyStringArrayList((List<String>) list);
+ UnsafeUtil.putObject(message, pos, list);
+ }
+ return list;
+ }
+
+ /**
+ * Determines whether to issue tableswitch or lookupswitch for the mergeFrom method.
+ *
+ * @see #shouldUseTableSwitch(int, int, int)
+ */
+ public static boolean shouldUseTableSwitch(List<FieldInfo> fields) {
+ // Determine whether to issue a tableswitch or a lookupswitch
+ // instruction.
+ if (fields.isEmpty()) {
+ return false;
+ }
+
+ int lo = fields.get(0).getFieldNumber();
+ int hi = fields.get(fields.size() - 1).getFieldNumber();
+ return shouldUseTableSwitch(lo, hi, fields.size());
+ }
+
+ /**
+ * Determines whether to issue tableswitch or lookupswitch for the mergeFrom method. This is based
+ * on the <a href=
+ * "http://hg.openjdk.java.net/jdk8/jdk8/langtools/file/30db5e0aaf83/src/share/classes/com/sun/tools/javac/jvm/Gen.java#l1159">
+ * logic in the JDK</a>.
+ *
+ * @param lo the lowest fieldNumber contained within the message.
+ * @param hi the higest fieldNumber contained within the message.
+ * @param numFields the total number of fields in the message.
+ * @return {@code true} if tableswitch should be used, rather than lookupswitch.
+ */
+ public static boolean shouldUseTableSwitch(int lo, int hi, int numFields) {
+ long tableSpaceCost = 4 + ((long) hi - lo + 1); // words
+ long tableTimeCost = 3; // comparisons
+ long lookupSpaceCost = 3 + 2 * (long) numFields;
+ long lookupTimeCost = numFields;
+ return tableSpaceCost + 3 * tableTimeCost <= lookupSpaceCost + 3 * lookupTimeCost;
+ }
+
+ private static Class<?> getAbstractMessageClass() {
+ try {
+ return Class.forName("com.google.protobuf.AbstractMessage");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static Class<?> getGeneratedMessageClass() {
+ try {
+ return Class.forName("com.google.protobuf.GeneratedMessage");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static Class<?> getUnknownFieldSetClass() {
+ try {
+ return Class.forName("com.google.protobuf.UnknownFieldSet");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static long unknownFieldOffset() {
+ try {
+ if (GENERATED_MESSAGE_CLASS != null) {
+ Field field = GENERATED_MESSAGE_CLASS.getDeclaredField("unknownFields");
+ return UnsafeUtil.objectFieldOffset(field);
+ }
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+
+ private static long unknownFieldLiteOffset() {
+ try {
+ Field field = GeneratedMessageLite.class.getDeclaredField("unknownFields");
+ return UnsafeUtil.objectFieldOffset(field);
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+
+ private static Object defaultUnknownFieldSet() {
+ try {
+ if (UNKNOWN_FIELD_SET_CLASS != null) {
+ Method method = UNKNOWN_FIELD_SET_CLASS.getDeclaredMethod("getDefaultInstance");
+ return method.invoke(null);
+ }
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return null;
+ }
+
+ private static long getMemoizedSizeFieldOffset() {
+ try {
+ if (ABSTRACT_MESSAGE_CLASS != null) {
+ Field field = ABSTRACT_MESSAGE_CLASS.getDeclaredField("memoizedSize");
+ return UnsafeUtil.objectFieldOffset(field);
+ }
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+
+ private static long getLiteMemoizedSizeFieldOffset() {
+ try {
+ Field field = GeneratedMessageLite.class.getDeclaredField("memoizedSerializedSize");
+ return UnsafeUtil.objectFieldOffset(field);
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+}