diff options
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.java | 516 |
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; + } +} |