diff options
Diffstat (limited to 'java/core/src/main/java/com/google/protobuf/MapEntryLite.java')
-rw-r--r-- | java/core/src/main/java/com/google/protobuf/MapEntryLite.java | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/MapEntryLite.java b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java new file mode 100644 index 00000000..22aef8f9 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java @@ -0,0 +1,226 @@ +// 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 java.io.IOException; +import java.util.AbstractMap; +import java.util.Map; + +/** + * Implements the lite version of map entry messages. + * + * This class serves as an utility class to help do serialization/parsing of + * map entries. It's used in generated code and also in the full version + * MapEntry message. + * + * Protobuf internal. Users shouldn't use. + */ +public class MapEntryLite<K, V> { + + static class Metadata<K, V> { + public final WireFormat.FieldType keyType; + public final K defaultKey; + public final WireFormat.FieldType valueType; + public final V defaultValue; + + public Metadata( + WireFormat.FieldType keyType, K defaultKey, + WireFormat.FieldType valueType, V defaultValue) { + this.keyType = keyType; + this.defaultKey = defaultKey; + this.valueType = valueType; + this.defaultValue = defaultValue; + } + } + + private static final int KEY_FIELD_NUMBER = 1; + private static final int VALUE_FIELD_NUMBER = 2; + + private final Metadata<K, V> metadata; + private final K key; + private final V value; + + /** Creates a default MapEntryLite message instance. */ + private MapEntryLite( + WireFormat.FieldType keyType, K defaultKey, + WireFormat.FieldType valueType, V defaultValue) { + this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue); + this.key = defaultKey; + this.value = defaultValue; + } + + /** Creates a new MapEntryLite message. */ + private MapEntryLite(Metadata<K, V> metadata, K key, V value) { + this.metadata = metadata; + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + /** + * Creates a default MapEntryLite message instance. + * + * This method is used by generated code to create the default instance for + * a map entry message. The created default instance should be used to create + * new map entry messages of the same type. For each map entry message, only + * one default instance should be created. + */ + public static <K, V> MapEntryLite<K, V> newDefaultInstance( + WireFormat.FieldType keyType, K defaultKey, + WireFormat.FieldType valueType, V defaultValue) { + return new MapEntryLite<K, V>( + keyType, defaultKey, valueType, defaultValue); + } + + static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value) + throws IOException { + FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key); + FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value); + } + + static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) { + return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key) + + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value); + } + + @SuppressWarnings("unchecked") + static <T> T parseField( + CodedInputStream input, ExtensionRegistryLite extensionRegistry, + WireFormat.FieldType type, T value) throws IOException { + switch (type) { + case MESSAGE: + MessageLite.Builder subBuilder = ((MessageLite) value).toBuilder(); + input.readMessage(subBuilder, extensionRegistry); + return (T) subBuilder.buildPartial(); + case ENUM: + return (T) (java.lang.Integer) input.readEnum(); + case GROUP: + throw new RuntimeException("Groups are not allowed in maps."); + default: + return (T) FieldSet.readPrimitiveField(input, type, true); + } + } + + /** + * Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite} + * to the output stream. This helper method avoids allocation of a {@link MapEntryLite} + * built with a key and value and is called from generated code directly. + */ + public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value) + throws IOException { + output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeUInt32NoTag(computeSerializedSize(metadata, key, value)); + writeTo(output, metadata, key, value); + } + + /** + * Computes the message size for the provided key and value as though they were wrapped + * by a {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite} + * built with a key and value and is called from generated code directly. + */ + public int computeMessageSize(int fieldNumber, K key, V value) { + return CodedOutputStream.computeTagSize(fieldNumber) + + CodedOutputStream.computeLengthDelimitedFieldSize( + computeSerializedSize(metadata, key, value)); + } + + /** + * Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation + * so using {@link #parseInto} is preferred if possible. + */ + public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry) + throws IOException { + return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry); + } + + static <K, V> Map.Entry<K, V> parseEntry( + CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry) + throws IOException{ + K key = metadata.defaultKey; + V value = metadata.defaultValue; + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) { + key = parseField(input, extensionRegistry, metadata.keyType, key); + } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) { + value = parseField(input, extensionRegistry, metadata.valueType, value); + } else { + if (!input.skipField(tag)) { + break; + } + } + } + return new AbstractMap.SimpleImmutableEntry<K, V>(key, value); + } + + /** + * Parses an entry off of the input into the map. This helper avoids allocaton of a + * {@link MapEntryLite} by parsing directly into the provided {@link MapFieldLite}. + */ + public void parseInto( + MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException { + int length = input.readRawVarint32(); + final int oldLimit = input.pushLimit(length); + K key = metadata.defaultKey; + V value = metadata.defaultValue; + + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) { + key = parseField(input, extensionRegistry, metadata.keyType, key); + } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) { + value = parseField(input, extensionRegistry, metadata.valueType, value); + } else { + if (!input.skipField(tag)) { + break; + } + } + } + + input.checkLastTagWas(0); + input.popLimit(oldLimit); + map.put(key, value); + } +} |