• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import java.io.IOException;
11 import java.util.AbstractMap;
12 import java.util.Map;
13 
14 /**
15  * Implements the lite version of map entry messages.
16  *
17  * <p>This class serves as an utility class to help do serialization/parsing of map entries. It's
18  * used in generated code and also in the full version MapEntry message.
19  *
20  * <p>Protobuf internal. Users shouldn't use.
21  */
22 public class MapEntryLite<K, V> {
23 
24   static class Metadata<K, V> {
25     public final WireFormat.FieldType keyType;
26     public final K defaultKey;
27     public final WireFormat.FieldType valueType;
28     public final V defaultValue;
29 
Metadata( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)30     public Metadata(
31         WireFormat.FieldType keyType,
32         K defaultKey,
33         WireFormat.FieldType valueType,
34         V defaultValue) {
35       this.keyType = keyType;
36       this.defaultKey = defaultKey;
37       this.valueType = valueType;
38       this.defaultValue = defaultValue;
39     }
40   }
41 
42   private static final int KEY_FIELD_NUMBER = 1;
43   private static final int VALUE_FIELD_NUMBER = 2;
44 
45   private final Metadata<K, V> metadata;
46   private final K key;
47   private final V value;
48 
49   /** Creates a default MapEntryLite message instance. */
MapEntryLite( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)50   private MapEntryLite(
51       WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) {
52     this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue);
53     this.key = defaultKey;
54     this.value = defaultValue;
55   }
56 
57   /** Creates a new MapEntryLite message. */
MapEntryLite(Metadata<K, V> metadata, K key, V value)58   private MapEntryLite(Metadata<K, V> metadata, K key, V value) {
59     this.metadata = metadata;
60     this.key = key;
61     this.value = value;
62   }
63 
getKey()64   public K getKey() {
65     return key;
66   }
67 
getValue()68   public V getValue() {
69     return value;
70   }
71 
72   /**
73    * Creates a default MapEntryLite message instance.
74    *
75    * <p>This method is used by generated code to create the default instance for a map entry
76    * message. The created default instance should be used to create new map entry messages of the
77    * same type. For each map entry message, only one default instance should be created.
78    */
newDefaultInstance( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)79   public static <K, V> MapEntryLite<K, V> newDefaultInstance(
80       WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) {
81     return new MapEntryLite<K, V>(keyType, defaultKey, valueType, defaultValue);
82   }
83 
writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)84   static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)
85       throws IOException {
86     FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key);
87     FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value);
88   }
89 
computeSerializedSize(Metadata<K, V> metadata, K key, V value)90   static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) {
91     return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key)
92         + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value);
93   }
94 
95   @SuppressWarnings("unchecked")
parseField( CodedInputStream input, ExtensionRegistryLite extensionRegistry, WireFormat.FieldType type, T value)96   static <T> T parseField(
97       CodedInputStream input,
98       ExtensionRegistryLite extensionRegistry,
99       WireFormat.FieldType type,
100       T value)
101       throws IOException {
102     switch (type) {
103       case MESSAGE:
104         MessageLite.Builder subBuilder = ((MessageLite) value).toBuilder();
105         input.readMessage(subBuilder, extensionRegistry);
106         return (T) subBuilder.buildPartial();
107       case ENUM:
108         return (T) (java.lang.Integer) input.readEnum();
109       case GROUP:
110         throw new RuntimeException("Groups are not allowed in maps.");
111       default:
112         return (T) FieldSet.readPrimitiveField(input, type, true);
113     }
114   }
115 
116   /**
117    * Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite} to
118    * the output stream. This helper method avoids allocation of a {@link MapEntryLite} built with a
119    * key and value and is called from generated code directly.
120    */
serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)121   public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)
122       throws IOException {
123     output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
124     output.writeUInt32NoTag(computeSerializedSize(metadata, key, value));
125     writeTo(output, metadata, key, value);
126   }
127 
128   /**
129    * Computes the message size for the provided key and value as though they were wrapped by a
130    * {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite} built with
131    * a key and value and is called from generated code directly.
132    */
computeMessageSize(int fieldNumber, K key, V value)133   public int computeMessageSize(int fieldNumber, K key, V value) {
134     return CodedOutputStream.computeTagSize(fieldNumber)
135         + CodedOutputStream.computeLengthDelimitedFieldSize(
136             computeSerializedSize(metadata, key, value));
137   }
138 
139   /**
140    * Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation so
141    * using {@link #parseInto} is preferred if possible.
142    */
parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)143   public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)
144       throws IOException {
145     return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry);
146   }
147 
parseEntry( CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)148   static <K, V> Map.Entry<K, V> parseEntry(
149       CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)
150       throws IOException {
151     K key = metadata.defaultKey;
152     V value = metadata.defaultValue;
153     while (true) {
154       int tag = input.readTag();
155       if (tag == 0) {
156         break;
157       }
158       if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
159         key = parseField(input, extensionRegistry, metadata.keyType, key);
160       } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
161         value = parseField(input, extensionRegistry, metadata.valueType, value);
162       } else {
163         if (!input.skipField(tag)) {
164           break;
165         }
166       }
167     }
168     return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
169   }
170 
171   /**
172    * Parses an entry off of the input into the map. This helper avoids allocation of a {@link
173    * MapEntryLite} by parsing directly into the provided {@link MapFieldLite}.
174    */
parseInto( MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)175   public void parseInto(
176       MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
177       throws IOException {
178     int length = input.readRawVarint32();
179     final int oldLimit = input.pushLimit(length);
180     K key = metadata.defaultKey;
181     V value = metadata.defaultValue;
182 
183     while (true) {
184       int tag = input.readTag();
185       if (tag == 0) {
186         break;
187       }
188       if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
189         key = parseField(input, extensionRegistry, metadata.keyType, key);
190       } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
191         value = parseField(input, extensionRegistry, metadata.valueType, value);
192       } else {
193         if (!input.skipField(tag)) {
194           break;
195         }
196       }
197     }
198 
199     input.checkLastTagWas(0);
200     input.popLimit(oldLimit);
201     map.put(key, value);
202   }
203 
204   /** For experimental runtime internal use only. */
getMetadata()205   Metadata<K, V> getMetadata() {
206     return metadata;
207   }
208 }
209