diff options
author | liujisi@google.com <liujisi@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2010-11-02 13:14:58 +0000 |
---|---|---|
committer | liujisi@google.com <liujisi@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2010-11-02 13:14:58 +0000 |
commit | 33165fe0d5c265c92f2a67fc2b437b567c24e294 (patch) | |
tree | 52def0850ddd2e976da238d1a437fbda79c96e44 /java/src/main/java/com/google/protobuf/FieldSet.java | |
parent | 80aa23df6c63750e8cdfdcf3996fbc37d63cac61 (diff) | |
download | protobuf-33165fe0d5c265c92f2a67fc2b437b567c24e294.tar.gz protobuf-33165fe0d5c265c92f2a67fc2b437b567c24e294.tar.bz2 protobuf-33165fe0d5c265c92f2a67fc2b437b567c24e294.zip |
Submit recent changes from internal branch. See CHANGES.txt for more details.
Diffstat (limited to 'java/src/main/java/com/google/protobuf/FieldSet.java')
-rw-r--r-- | java/src/main/java/com/google/protobuf/FieldSet.java | 246 |
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. |