aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/src/main/java/com/google/protobuf/FieldSet.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/main/java/com/google/protobuf/FieldSet.java')
-rw-r--r--java/src/main/java/com/google/protobuf/FieldSet.java246
1 files changed, 161 insertions, 85 deletions
diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java
index bc1bb797..a85dbaa6 100644
--- a/java/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/src/main/java/com/google/protobuf/FieldSet.java
@@ -33,7 +33,6 @@ package com.google.protobuf;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
-import java.util.TreeMap;
import java.util.List;
import java.util.Map;
import java.io.IOException;
@@ -67,16 +66,12 @@ final class FieldSet<FieldDescriptorType extends
MessageLite.Builder to, MessageLite from);
}
- private Map<FieldDescriptorType, Object> fields;
+ private final SmallSortedMap<FieldDescriptorType, Object> fields;
+ private boolean isImmutable;
/** Construct a new FieldSet. */
private FieldSet() {
- // Use a TreeMap because fields need to be in canonical order when
- // serializing.
- // TODO(kenton): Maybe use some sort of sparse array instead? It would
- // even make sense to store the first 16 or so tags in a flat array
- // to make DynamicMessage faster.
- fields = new TreeMap<FieldDescriptorType, Object>();
+ this.fields = SmallSortedMap.newFieldMap(16);
}
/**
@@ -84,7 +79,8 @@ final class FieldSet<FieldDescriptorType extends
* DEFAULT_INSTANCE.
*/
private FieldSet(final boolean dummy) {
- this.fields = Collections.emptyMap();
+ this.fields = SmallSortedMap.newFieldMap(0);
+ makeImmutable();
}
/** Construct a new FieldSet. */
@@ -105,14 +101,45 @@ final class FieldSet<FieldDescriptorType extends
/** Make this FieldSet immutable from this point forward. */
@SuppressWarnings("unchecked")
public void makeImmutable() {
- for (final Map.Entry<FieldDescriptorType, Object> entry:
- fields.entrySet()) {
- if (entry.getKey().isRepeated()) {
- final List value = (List)entry.getValue();
- fields.put(entry.getKey(), Collections.unmodifiableList(value));
- }
+ if (isImmutable) {
+ return;
}
- fields = Collections.unmodifiableMap(fields);
+ fields.makeImmutable();
+ isImmutable = true;
+ }
+
+ /**
+ * Retuns whether the FieldSet is immutable. This is true if it is the
+ * {@link #emptySet} or if {@link #makeImmutable} were called.
+ *
+ * @return whether the FieldSet is immutable.
+ */
+ public boolean isImmutable() {
+ return isImmutable;
+ }
+
+ /**
+ * Clones the FieldSet. The returned FieldSet will be mutable even if the
+ * original FieldSet was immutable.
+ *
+ * @return the newly cloned FieldSet
+ */
+ @Override
+ public FieldSet<FieldDescriptorType> clone() {
+ // We can't just call fields.clone because List objects in the map
+ // should not be shared.
+ FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
+ for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+ Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
+ FieldDescriptorType descriptor = entry.getKey();
+ clone.setField(descriptor, entry.getValue());
+ }
+ for (Map.Entry<FieldDescriptorType, Object> entry :
+ fields.getOverflowEntries()) {
+ FieldDescriptorType descriptor = entry.getKey();
+ clone.setField(descriptor, entry.getValue());
+ }
+ return clone;
}
// =================================================================
@@ -126,12 +153,13 @@ final class FieldSet<FieldDescriptorType extends
* Get a simple map containing all the fields.
*/
public Map<FieldDescriptorType, Object> getAllFields() {
- return Collections.unmodifiableMap(fields);
+ return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
}
/**
- * Get an iterator to the field map. This iterator should not be leaked
- * out of the protobuf library as it is not protected from mutation.
+ * Get an iterator to the field map. This iterator should not be leaked out
+ * of the protobuf library as it is not protected from mutation when
+ * fields is not immutable.
*/
public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
return fields.entrySet().iterator();
@@ -336,27 +364,39 @@ final class FieldSet<FieldDescriptorType extends
* aren't actually present in the set, it is up to the caller to check
* that all required fields are present.
*/
- @SuppressWarnings("unchecked")
public boolean isInitialized() {
- for (final Map.Entry<FieldDescriptorType, Object> entry:
- fields.entrySet()) {
- final FieldDescriptorType descriptor = entry.getKey();
- if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
- if (descriptor.isRepeated()) {
- for (final MessageLite element:
- (List<MessageLite>) entry.getValue()) {
- if (!element.isInitialized()) {
- return false;
- }
- }
- } else {
- if (!((MessageLite) entry.getValue()).isInitialized()) {
+ for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+ if (!isInitialized(fields.getArrayEntryAt(i))) {
+ return false;
+ }
+ }
+ for (final Map.Entry<FieldDescriptorType, Object> entry :
+ fields.getOverflowEntries()) {
+ if (!isInitialized(entry)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean isInitialized(
+ final Map.Entry<FieldDescriptorType, Object> entry) {
+ final FieldDescriptorType descriptor = entry.getKey();
+ if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
+ if (descriptor.isRepeated()) {
+ for (final MessageLite element:
+ (List<MessageLite>) entry.getValue()) {
+ if (!element.isInitialized()) {
return false;
}
}
+ } else {
+ if (!((MessageLite) entry.getValue()).isInitialized()) {
+ return false;
+ }
}
}
-
return true;
}
@@ -378,39 +418,48 @@ final class FieldSet<FieldDescriptorType extends
/**
* Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
*/
- @SuppressWarnings("unchecked")
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
- for (final Map.Entry<FieldDescriptorType, Object> entry:
- other.fields.entrySet()) {
- final FieldDescriptorType descriptor = entry.getKey();
- final Object otherValue = entry.getValue();
+ for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
+ mergeFromField(other.fields.getArrayEntryAt(i));
+ }
+ for (final Map.Entry<FieldDescriptorType, Object> entry :
+ other.fields.getOverflowEntries()) {
+ mergeFromField(entry);
+ }
+ }
- if (descriptor.isRepeated()) {
- Object value = fields.get(descriptor);
- if (value == null) {
- // Our list is empty, but we still need to make a defensive copy of
- // the other list since we don't know if the other FieldSet is still
- // mutable.
- fields.put(descriptor, new ArrayList((List) otherValue));
- } else {
- // Concatenate the lists.
- ((List) value).addAll((List) otherValue);
- }
- } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
- Object value = fields.get(descriptor);
- if (value == null) {
- fields.put(descriptor, otherValue);
- } else {
- // Merge the messages.
- fields.put(descriptor,
- descriptor.internalMergeFrom(
- ((MessageLite) value).toBuilder(), (MessageLite) otherValue)
- .build());
- }
+ @SuppressWarnings("unchecked")
+ private void mergeFromField(
+ final Map.Entry<FieldDescriptorType, Object> entry) {
+ final FieldDescriptorType descriptor = entry.getKey();
+ final Object otherValue = entry.getValue();
+ if (descriptor.isRepeated()) {
+ Object value = fields.get(descriptor);
+ if (value == null) {
+ // Our list is empty, but we still need to make a defensive copy of
+ // the other list since we don't know if the other FieldSet is still
+ // mutable.
+ fields.put(descriptor, new ArrayList((List) otherValue));
} else {
+ // Concatenate the lists.
+ ((List) value).addAll((List) otherValue);
+ }
+ } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
+ Object value = fields.get(descriptor);
+ if (value == null) {
fields.put(descriptor, otherValue);
+ } else {
+ // Merge the messages.
+ fields.put(
+ descriptor,
+ descriptor.internalMergeFrom(
+ ((MessageLite) value).toBuilder(), (MessageLite) otherValue)
+ .build());
}
+
+ } else {
+ fields.put(descriptor, otherValue);
}
}
@@ -468,8 +517,13 @@ final class FieldSet<FieldDescriptorType extends
/** See {@link Message#writeTo(CodedOutputStream)}. */
public void writeTo(final CodedOutputStream output)
throws IOException {
- for (final Map.Entry<FieldDescriptorType, Object> entry:
- fields.entrySet()) {
+ for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+ final Map.Entry<FieldDescriptorType, Object> entry =
+ fields.getArrayEntryAt(i);
+ writeField(entry.getKey(), entry.getValue(), output);
+ }
+ for (final Map.Entry<FieldDescriptorType, Object> entry :
+ fields.getOverflowEntries()) {
writeField(entry.getKey(), entry.getValue(), output);
}
}
@@ -479,16 +533,25 @@ final class FieldSet<FieldDescriptorType extends
*/
public void writeMessageSetTo(final CodedOutputStream output)
throws IOException {
- for (final Map.Entry<FieldDescriptorType, Object> entry:
- fields.entrySet()) {
- final FieldDescriptorType descriptor = entry.getKey();
- if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
- !descriptor.isRepeated() && !descriptor.isPacked()) {
- output.writeMessageSetExtension(entry.getKey().getNumber(),
- (MessageLite) entry.getValue());
- } else {
- writeField(descriptor, entry.getValue(), output);
- }
+ for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+ writeMessageSetTo(fields.getArrayEntryAt(i), output);
+ }
+ for (final Map.Entry<FieldDescriptorType, Object> entry :
+ fields.getOverflowEntries()) {
+ writeMessageSetTo(entry, output);
+ }
+ }
+
+ private void writeMessageSetTo(
+ final Map.Entry<FieldDescriptorType, Object> entry,
+ final CodedOutputStream output) throws IOException {
+ final FieldDescriptorType descriptor = entry.getKey();
+ if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
+ !descriptor.isRepeated() && !descriptor.isPacked()) {
+ output.writeMessageSetExtension(entry.getKey().getNumber(),
+ (MessageLite) entry.getValue());
+ } else {
+ writeField(descriptor, entry.getValue(), output);
}
}
@@ -593,8 +656,13 @@ final class FieldSet<FieldDescriptorType extends
*/
public int getSerializedSize() {
int size = 0;
- for (final Map.Entry<FieldDescriptorType, Object> entry:
- fields.entrySet()) {
+ for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+ final Map.Entry<FieldDescriptorType, Object> entry =
+ fields.getArrayEntryAt(i);
+ size += computeFieldSize(entry.getKey(), entry.getValue());
+ }
+ for (final Map.Entry<FieldDescriptorType, Object> entry :
+ fields.getOverflowEntries()) {
size += computeFieldSize(entry.getKey(), entry.getValue());
}
return size;
@@ -605,20 +673,28 @@ final class FieldSet<FieldDescriptorType extends
*/
public int getMessageSetSerializedSize() {
int size = 0;
- for (final Map.Entry<FieldDescriptorType, Object> entry:
- fields.entrySet()) {
- final FieldDescriptorType descriptor = entry.getKey();
- if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
- !descriptor.isRepeated() && !descriptor.isPacked()) {
- size += CodedOutputStream.computeMessageSetExtensionSize(
- entry.getKey().getNumber(), (MessageLite) entry.getValue());
- } else {
- size += computeFieldSize(descriptor, entry.getValue());
- }
+ for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+ size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
+ }
+ for (final Map.Entry<FieldDescriptorType, Object> entry :
+ fields.getOverflowEntries()) {
+ size += getMessageSetSerializedSize(entry);
}
return size;
}
+ private int getMessageSetSerializedSize(
+ final Map.Entry<FieldDescriptorType, Object> entry) {
+ final FieldDescriptorType descriptor = entry.getKey();
+ if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
+ !descriptor.isRepeated() && !descriptor.isPacked()) {
+ return CodedOutputStream.computeMessageSetExtensionSize(
+ entry.getKey().getNumber(), (MessageLite) entry.getValue());
+ } else {
+ return computeFieldSize(descriptor, entry.getValue());
+ }
+ }
+
/**
* Compute the number of bytes that would be needed to encode a
* single tag/value pair of arbitrary type.