diff options
author | Bo Yang <teboring@google.com> | 2015-05-21 14:28:59 -0700 |
---|---|---|
committer | Bo Yang <teboring@google.com> | 2015-05-21 19:32:02 -0700 |
commit | 5db217305f37a79eeccd70f000088a06ec82fcec (patch) | |
tree | be53dcf0c0b47ef9178ab8a6fa5c1946ee84a28f /java/src/main | |
parent | 56095026ccc2f755a6fdb296e30c3ddec8f556a2 (diff) | |
download | protobuf-5db217305f37a79eeccd70f000088a06ec82fcec.tar.gz protobuf-5db217305f37a79eeccd70f000088a06ec82fcec.tar.bz2 protobuf-5db217305f37a79eeccd70f000088a06ec82fcec.zip |
down-integrate internal changes
Diffstat (limited to 'java/src/main')
16 files changed, 2427 insertions, 178 deletions
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java index aac4fa77..12384983 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -31,8 +31,8 @@ package com.google.protobuf; import java.io.FilterInputStream; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; @@ -109,6 +109,11 @@ public abstract class AbstractMessageLite implements MessageLite { } } + protected static <T> void addAll(final Iterable<T> values, + final Collection<? super T> list) { + Builder.addAll(values, list); + } + /** * A partial implementation of the {@link Message.Builder} interface which * implements as many methods of that interface as possible in terms of @@ -320,12 +325,15 @@ public abstract class AbstractMessageLite implements MessageLite { * Adds the {@code values} to the {@code list}. This is a helper method * used by generated code. Users should ignore it. * - * @throws NullPointerException if any of the elements of {@code values} is - * null. When that happens, some elements of {@code values} may have already - * been added to the result {@code list}. + * @throws NullPointerException if {@code values} or any of the elements of + * {@code values} is null. When that happens, some elements of + * {@code values} may have already been added to the result {@code list}. */ protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) { + if (values == null) { + throw new NullPointerException(); + } if (values instanceof LazyStringList) { // For StringOrByteStringLists, check the underlying elements to avoid // forcing conversions of ByteStrings to Strings. diff --git a/java/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java new file mode 100644 index 00000000..bb6446b2 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java @@ -0,0 +1,136 @@ +// 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.util.AbstractList; +import java.util.Collection; + +/** + * An abstract implementation of {@link ProtobufList} which manages mutability semantics. All mutate + * methods are check if the list is mutable before proceeding. Subclasses must invoke + * {@link #ensureIsMutable()} manually when overriding those methods. + */ +abstract class AbstractProtobufList<E> extends AbstractList<E> implements ProtobufList<E> { + + /** + * Whether or not this list is modifiable. + */ + private boolean isMutable; + + /** + * Constructs a mutable list by default. + */ + AbstractProtobufList() { + isMutable = true; + } + + @Override + public boolean add(E e) { + ensureIsMutable(); + return super.add(e); + } + + @Override + public void add(int index, E element) { + ensureIsMutable(); + super.add(index, element); + } + + @Override + public boolean addAll(Collection<? extends E> c) { + ensureIsMutable(); + return super.addAll(c); + } + + @Override + public boolean addAll(int index, Collection<? extends E> c) { + ensureIsMutable(); + return super.addAll(index, c); + } + + @Override + public void clear() { + ensureIsMutable(); + super.clear(); + } + + @Override + public boolean isModifiable() { + return isMutable; + } + + @Override + public final void makeImmutable() { + isMutable = false; + } + + @Override + public E remove(int index) { + ensureIsMutable(); + return super.remove(index); + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + return super.remove(o); + } + + @Override + public boolean removeAll(Collection<?> c) { + ensureIsMutable(); + return super.removeAll(c); + } + + @Override + public boolean retainAll(Collection<?> c) { + ensureIsMutable(); + return super.retainAll(c); + } + + @Override + public E set(int index, E element) { + ensureIsMutable(); + return super.set(index, element); + } + + /** + * Throws an {@link UnsupportedOperationException} if the list is immutable. Subclasses are + * responsible for invoking this method on mutate operations. + */ + protected void ensureIsMutable() { + if (!isMutable) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/java/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/src/main/java/com/google/protobuf/BooleanArrayList.java new file mode 100644 index 00000000..45492d2f --- /dev/null +++ b/java/src/main/java/com/google/protobuf/BooleanArrayList.java @@ -0,0 +1,244 @@ +// 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.BooleanList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link BooleanList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class BooleanArrayList + extends AbstractProtobufList<Boolean> implements BooleanList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static BooleanArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private boolean[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code BooleanArrayList}. + */ + BooleanArrayList() { + array = new boolean[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code BooleanArrayList} containing the same elements as + * {@code other}. + */ + BooleanArrayList(List<Boolean> other) { + if (other instanceof BooleanArrayList) { + BooleanArrayList list = (BooleanArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new boolean[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Boolean get(int index) { + return getBoolean(index); + } + + @Override + public boolean getBoolean(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Boolean set(int index, Boolean element) { + return setBoolean(index, element); + } + + @Override + public boolean setBoolean(int index, boolean element) { + ensureIsMutable(); + ensureIndexInRange(index); + boolean previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Boolean element) { + addBoolean(index, element); + } + + /** + * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. + */ + @Override + public void addBoolean(boolean element) { + addBoolean(size, element); + } + + /** + * Like {@link #add(int, Boolean)} but more efficient in that it doesn't box the element. + */ + private void addBoolean(int index, boolean element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + boolean[] newArray = new boolean[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection<? extends Boolean> collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another BooleanArrayList to avoid boxing elements. + if (!(collection instanceof BooleanArrayList)) { + return super.addAll(collection); + } + + BooleanArrayList list = (BooleanArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Boolean remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + boolean value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index b60bd36b..3658410c 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -644,6 +644,30 @@ public final class Descriptors { return false; } + /** Determines if the given field number is reserved. */ + public boolean isReservedNumber(final int number) { + for (final DescriptorProto.ReservedRange range : + proto.getReservedRangeList()) { + if (range.getStart() <= number && number < range.getEnd()) { + return true; + } + } + return false; + } + + /** Determines if the given field name is reserved. */ + public boolean isReservedName(final String name) { + if (name == null) { + throw new NullPointerException(); + } + for (final String reservedName : proto.getReservedNameList()) { + if (reservedName.equals(name)) { + return true; + } + } + return false; + } + /** * Indicates whether the message can be extended. That is, whether it has * any "extensions x to y" ranges declared on it. @@ -917,9 +941,18 @@ public final class Descriptors { return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; } - /** Does this field have the {@code [packed = true]} option? */ + /** Does this field have the {@code [packed = true]} option or is this field + * packable in proto3 and not explicitly setted to unpacked? + */ public boolean isPacked() { - return getOptions().getPacked(); + if (!isPackable()) { + return false; + } + if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) { + return getOptions().getPacked(); + } else { + return !getOptions().hasPacked() || getOptions().getPacked(); + } } /** Can this field be packed? i.e. is it a repeated primitive field? */ @@ -2317,6 +2350,11 @@ public final class Descriptors { public int getFieldCount() { return fieldCount; } + /** Get a list of this message type's fields. */ + public List<FieldDescriptor> getFields() { + return Collections.unmodifiableList(Arrays.asList(fields)); + } + public FieldDescriptor getField(int index) { return fields[index]; } diff --git a/java/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/src/main/java/com/google/protobuf/DoubleArrayList.java new file mode 100644 index 00000000..90ebe109 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/DoubleArrayList.java @@ -0,0 +1,243 @@ +// 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.DoubleList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link DoubleList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class DoubleArrayList + extends AbstractProtobufList<Double> implements DoubleList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static DoubleArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private double[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code DoubleArrayList}. + */ + DoubleArrayList() { + array = new double[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}. + */ + DoubleArrayList(List<Double> other) { + if (other instanceof DoubleArrayList) { + DoubleArrayList list = (DoubleArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new double[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Double get(int index) { + return getDouble(index); + } + + @Override + public double getDouble(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Double set(int index, Double element) { + return setDouble(index, element); + } + + @Override + public double setDouble(int index, double element) { + ensureIsMutable(); + ensureIndexInRange(index); + double previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Double element) { + addDouble(index, element); + } + + /** + * Like {@link #add(Double)} but more efficient in that it doesn't box the element. + */ + @Override + public void addDouble(double element) { + addDouble(size, element); + } + + /** + * Like {@link #add(int, Double)} but more efficient in that it doesn't box the element. + */ + private void addDouble(int index, double element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + double[] newArray = new double[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection<? extends Double> collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another DoubleArrayList to avoid boxing elements. + if (!(collection instanceof DoubleArrayList)) { + return super.addAll(collection); + } + + DoubleArrayList list = (DoubleArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Double remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + double value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/FloatArrayList.java b/java/src/main/java/com/google/protobuf/FloatArrayList.java new file mode 100644 index 00000000..293eaff6 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/FloatArrayList.java @@ -0,0 +1,242 @@ +// 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.FloatList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link FloatList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class FloatArrayList extends AbstractProtobufList<Float> implements FloatList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final FloatArrayList EMPTY_LIST = new FloatArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static FloatArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private float[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code FloatArrayList}. + */ + FloatArrayList() { + array = new float[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}. + */ + FloatArrayList(List<Float> other) { + if (other instanceof FloatArrayList) { + FloatArrayList list = (FloatArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new float[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Float get(int index) { + return getFloat(index); + } + + @Override + public float getFloat(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Float set(int index, Float element) { + return setFloat(index, element); + } + + @Override + public float setFloat(int index, float element) { + ensureIsMutable(); + ensureIndexInRange(index); + float previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Float element) { + addFloat(index, element); + } + + /** + * Like {@link #add(Float)} but more efficient in that it doesn't box the element. + */ + @Override + public void addFloat(float element) { + addFloat(size, element); + } + + /** + * Like {@link #add(int, Float)} but more efficient in that it doesn't box the element. + */ + private void addFloat(int index, float element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + float[] newArray = new float[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection<? extends Float> collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another FloatArrayList to avoid boxing elements. + if (!(collection instanceof FloatArrayList)) { + return super.addAll(collection); + } + + FloatArrayList list = (FloatArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Float remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + float value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index 6839c9dd..bd6bc463 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -30,6 +30,12 @@ package com.google.protobuf; +import com.google.protobuf.Internal.BooleanList; +import com.google.protobuf.Internal.DoubleList; +import com.google.protobuf.Internal.FloatList; +import com.google.protobuf.Internal.IntList; +import com.google.protobuf.Internal.LongList; +import com.google.protobuf.Internal.ProtobufList; import com.google.protobuf.WireFormat.FieldType; import java.io.IOException; @@ -76,7 +82,11 @@ public abstract class GeneratedMessageLite< private static final long serialVersionUID = 1L; /** For use by generated code only. */ - protected UnknownFieldSetLite unknownFields; + protected UnknownFieldSetLite unknownFields = + UnknownFieldSetLite.getDefaultInstance(); + + /** For use by generated code only. */ + protected int memoizedSerializedSize = -1; @SuppressWarnings("unchecked") // Guaranteed by runtime. public final Parser<MessageType> getParserForType() { @@ -109,10 +119,67 @@ public abstract class GeneratedMessageLite< return unknownFields.mergeFieldFrom(tag, input); } - // The default behavior. If a message has required fields in its subtree, the - // generated code will override. - public boolean isInitialized() { - return true; + public final boolean isInitialized() { + return dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.TRUE) != null; + } + + public final BuilderType toBuilder() { + BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); + builder.mergeFrom((MessageType) this); + return builder; + } + + /** + * Defines which method path to invoke in {@link GeneratedMessageLite + * #dynamicMethod(MethodToInvoke, Object...)}. + * <p> + * For use by generated code only. + */ + public static enum MethodToInvoke { + IS_INITIALIZED, + PARSE_PARTIAL_FROM, + MERGE_FROM, + MAKE_IMMUTABLE, + NEW_INSTANCE, + NEW_BUILDER; + } + + /** + * A method that implements different types of operations described in {@link MethodToInvoke}. + * Theses different kinds of operations are required to implement message-level operations for + * builders in the runtime. This method bundles those operations to reduce the generated methods + * count. + * <ul> + * <li>{@code PARSE_PARTIAL_FROM} is parameterized with an {@link CodedInputStream} and + * {@link ExtensionRegistryLite}. It consumes the input stream, parsing the contents into the + * returned protocol buffer. If parsing throws an {@link InvalidProtocolBufferException}, the + * implementation wraps it in a RuntimeException + * <li>{@code NEW_INSTANCE} returns a new instance of the protocol buffer + * <li>{@code IS_INITIALIZED} is parameterized with a {@code Boolean} detailing whether to + * memoize. It returns {@code null} for false and the default instance for true. We optionally + * memoize to support the Builder case, where memoization is not desired. + * <li>{@code NEW_BUILDER} returns a {@code BuilderType} instance. + * <li>{@code MERGE_FROM} is parameterized with a {@code MessageType} and merges the fields from + * that instance into this instance. + * <li>{@code MAKE_IMMUTABLE} sets all internal fields to an immutable state. + * </ul> + * This method, plus the implementation of the Builder, enables the Builder class to be proguarded + * away entirely on Android. + * <p> + * For use by generated code only. + */ + protected abstract Object dynamicMethod( + MethodToInvoke method, + Object... args); + + /** + * Merge some unknown fields into the {@link UnknownFieldSetLite} for this + * message. + * + * <p>For use by generated code only. + */ + protected final void mergeUnknownFields(UnknownFieldSetLite unknownFields) { + this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); } @SuppressWarnings("unchecked") @@ -122,24 +189,37 @@ public abstract class GeneratedMessageLite< extends AbstractMessageLite.Builder<BuilderType> { private final MessageType defaultInstance; - - /** For use by generated code only. */ - protected UnknownFieldSetLite unknownFields = - UnknownFieldSetLite.getDefaultInstance(); + protected MessageType instance; + protected boolean isBuilt; protected Builder(MessageType defaultInstance) { this.defaultInstance = defaultInstance; + this.instance = (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + isBuilt = false; } - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - return true; + /** + * Called before any method that would mutate the builder to ensure that it correctly copies + * any state before the write happens to preserve immutability guarantees. + */ + protected void copyOnWrite() { + if (isBuilt) { + MessageType newInstance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + newInstance.dynamicMethod(MethodToInvoke.MERGE_FROM, instance); + instance = newInstance; + isBuilt = false; + } + } + + //@Override (Java 1.6 override semantics, but we must support 1.5) + public final boolean isInitialized() { + return GeneratedMessageLite.isInitialized(instance, false /* shouldMemoize */); } //@Override (Java 1.6 override semantics, but we must support 1.5) - public BuilderType clear() { - unknownFields = UnknownFieldSetLite.getDefaultInstance(); + public final BuilderType clear() { + // No need to copy on write since we're dropping the instance anyways. + instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); return (BuilderType) this; } @@ -151,8 +231,12 @@ public abstract class GeneratedMessageLite< return builder; } - /** All subclasses implement this. */ - public abstract MessageType buildPartial(); + //@Override (Java 1.6 override semantics, but we must support 1.5) + public MessageType buildPartial() { + instance.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + isBuilt = true; + return instance; + } //@Override (Java 1.6 override semantics, but we must support 1.5) public final MessageType build() { @@ -162,9 +246,13 @@ public abstract class GeneratedMessageLite< } return result; } - + /** All subclasses implement this. */ - public abstract BuilderType mergeFrom(MessageType message); + public BuilderType mergeFrom(MessageType message) { + copyOnWrite(); + instance.dynamicMethod(MethodToInvoke.MERGE_FROM, message); + return (BuilderType) this; + } public MessageType getDefaultInstanceForType() { return defaultInstance; @@ -181,18 +269,6 @@ public abstract class GeneratedMessageLite< int tag) throws IOException { return unknownFields.mergeFieldFrom(tag, input); } - - /** - * Merge some unknown fields into the {@link UnknownFieldSetLite} for this - * message. - * - * <p>For use by generated code only. - */ - protected final BuilderType mergeUnknownFields( - final UnknownFieldSetLite unknownFields) { - this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); - return (BuilderType) this; - } public BuilderType mergeFrom( com.google.protobuf.CodedInputStream input, @@ -259,19 +335,13 @@ public abstract class GeneratedMessageLite< */ protected FieldSet<ExtensionDescriptor> extensions = FieldSet.newFieldSet(); - // -1 => not memoized, 0 => false, 1 => true. - private byte memoizedIsInitialized = -1; - - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - if (memoizedIsInitialized == -1) { - memoizedIsInitialized = (byte) (extensions.isInitialized() ? 1 : 0); + protected final void mergeExtensionFields(final MessageType other) { + if (extensions.isImmutable()) { + extensions = extensions.clone(); } - - return memoizedIsInitialized == 1; + extensions.mergeFrom(((ExtendableMessage) other).extensions); } - + private void verifyExtensionContainingType( final GeneratedExtension<MessageType, ?> extension) { if (extension.getContainingTypeDefaultInstance() != @@ -420,46 +490,38 @@ public abstract class GeneratedMessageLite< implements ExtendableMessageOrBuilder<MessageType, BuilderType> { protected ExtendableBuilder(MessageType defaultInstance) { super(defaultInstance); - } - - private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet(); - private boolean extensionsIsMutable; - - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - return extensions.isInitialized(); + + // TODO(dweis): This is kind of an unnecessary clone since we construct a + // new instance in the parent constructor which makes the extensions + // immutable. This extra allocation shouldn't matter in practice + // though. + instance.extensions = instance.extensions.clone(); } // For immutable message conversion. void internalSetExtensionSet(FieldSet<ExtensionDescriptor> extensions) { - this.extensions = extensions; + copyOnWrite(); + instance.extensions = extensions; } - @Override - public BuilderType clear() { - extensions.clear(); - extensionsIsMutable = false; - return super.clear(); + // @Override (Java 1.6 override semantics, but we must support 1.5) + protected void copyOnWrite() { + if (!isBuilt) { + return; + } + + super.copyOnWrite(); + instance.extensions = instance.extensions.clone(); } - private void ensureExtensionsIsMutable() { - if (!extensionsIsMutable) { - extensions = extensions.clone(); - extensionsIsMutable = true; + // @Override (Java 1.6 override semantics, but we must support 1.5) + public final MessageType buildPartial() { + if (isBuilt) { + return instance; } - } - /** - * Called by the build code path to create a copy of the extensions for - * building the message. - * <p> - * For use by generated code only. - */ - protected final FieldSet<ExtensionDescriptor> buildExtensions() { - extensions.makeImmutable(); - extensionsIsMutable = false; - return extensions; + instance.extensions.makeImmutable(); + return super.buildPartial(); } private void verifyExtensionContainingType( @@ -477,22 +539,14 @@ public abstract class GeneratedMessageLite< //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> boolean hasExtension( final ExtensionLite<MessageType, Type> extension) { - GeneratedExtension<MessageType, Type> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return extensions.hasField(extensionLite.descriptor); + return instance.hasExtension(extension); } /** Get the number of elements in a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( final ExtensionLite<MessageType, List<Type>> extension) { - GeneratedExtension<MessageType, List<Type>> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return extensions.getRepeatedFieldCount(extensionLite.descriptor); + return instance.getExtensionCount(extension); } /** Get the value of an extension. */ @@ -500,16 +554,7 @@ public abstract class GeneratedMessageLite< @SuppressWarnings("unchecked") public final <Type> Type getExtension( final ExtensionLite<MessageType, Type> extension) { - GeneratedExtension<MessageType, Type> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - final Object value = extensions.getField(extensionLite.descriptor); - if (value == null) { - return extensionLite.defaultValue; - } else { - return (Type) extensionLite.fromFieldSetType(value); - } + return instance.getExtension(extension); } /** Get one element of a repeated extension. */ @@ -518,12 +563,7 @@ public abstract class GeneratedMessageLite< public final <Type> Type getExtension( final ExtensionLite<MessageType, List<Type>> extension, final int index) { - GeneratedExtension<MessageType, List<Type>> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return (Type) extensionLite.singularFromFieldSetType( - extensions.getRepeatedField(extensionLite.descriptor, index)); + return instance.getExtension(extension, index); } // This is implemented here only to work around an apparent bug in the @@ -542,9 +582,8 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.setField(extensionLite.descriptor, - extensionLite.toFieldSetType(value)); + copyOnWrite(); + instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value)); return (BuilderType) this; } @@ -556,9 +595,9 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.setRepeatedField(extensionLite.descriptor, index, - extensionLite.singularToFieldSetType(value)); + copyOnWrite(); + instance.extensions.setRepeatedField( + extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -570,9 +609,9 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.addRepeatedField(extensionLite.descriptor, - extensionLite.singularToFieldSetType(value)); + copyOnWrite(); + instance.extensions.addRepeatedField( + extensionLite.descriptor, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -582,20 +621,10 @@ public abstract class GeneratedMessageLite< GeneratedExtension<MessageType, ?> extensionLite = checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.clearField(extensionLite.descriptor); + copyOnWrite(); + instance.extensions.clearField(extensionLite.descriptor); return (BuilderType) this; } - - /** Called by subclasses to check if all extensions are initialized. */ - protected boolean extensionsAreInitialized() { - return extensions.isInitialized(); - } - - protected final void mergeExtensionFields(final MessageType other) { - ensureExtensionsIsMutable(); - extensions.mergeFrom(((ExtendableMessage) other).extensions); - } } //----------------------------------------------------------------- @@ -1113,4 +1142,133 @@ public abstract class GeneratedMessageLite< return (BuilderType) defaultInstance.toBuilder(); } } + + /** + * A static helper method for checking if a message is initialized, optionally memoizing. + * <p> + * For use by generated code only. + */ + protected static final <T extends GeneratedMessageLite<T, ?>> boolean isInitialized( + T message, boolean shouldMemoize) { + return message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, shouldMemoize) != null; + } + + protected static final <T extends GeneratedMessageLite<T, ?>> void makeImmutable(T message) { + message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + } + + /** + * A static helper method for parsing a partial from input using the extension registry and the + * instance. + */ + static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom( + T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + try { + return (T) instance.dynamicMethod( + MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry); + } catch (RuntimeException e) { + if (e.getCause() instanceof InvalidProtocolBufferException) { + throw (InvalidProtocolBufferException) e.getCause(); + } + throw e; + } + } + + /** + * A {@link Parser} implementation that delegates to the default instance. + * <p> + * For use by generated code only. + */ + protected static class DefaultInstanceBasedParser<T extends GeneratedMessageLite<T, ?>> + extends AbstractParser<T> { + + private T defaultInstance; + + public DefaultInstanceBasedParser(T defaultInstance) { + this.defaultInstance = defaultInstance; + } + + @Override + public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry); + } + } + + protected static IntList newIntList() { + return new IntArrayList(); + } + + protected static IntList newIntList(List<Integer> toCopy) { + return new IntArrayList(toCopy); + } + + protected static IntList emptyIntList() { + return IntArrayList.emptyList(); + } + + protected static LongList newLongList() { + return new LongArrayList(); + } + + protected static LongList newLongList(List<Long> toCopy) { + return new LongArrayList(toCopy); + } + + protected static LongList emptyLongList() { + return LongArrayList.emptyList(); + } + + protected static FloatList newFloatList() { + return new FloatArrayList(); + } + + protected static FloatList newFloatList(List<Float> toCopy) { + return new FloatArrayList(toCopy); + } + + protected static FloatList emptyFloatList() { + return FloatArrayList.emptyList(); + } + + protected static DoubleList newDoubleList() { + return new DoubleArrayList(); + } + + protected static DoubleList newDoubleList(List<Double> toCopy) { + return new DoubleArrayList(toCopy); + } + + protected static DoubleList emptyDoubleList() { + return DoubleArrayList.emptyList(); + } + + protected static BooleanList newBooleanList() { + return new BooleanArrayList(); + } + + protected static BooleanList newBooleanList(List<Boolean> toCopy) { + return new BooleanArrayList(toCopy); + } + + protected static BooleanList emptyBooleanList() { + return BooleanArrayList.emptyList(); + } + + protected static <E> ProtobufList<E> newProtobufList() { + return new ProtobufArrayList<E>(); + } + + protected static <E> ProtobufList<E> newProtobufList(List<E> toCopy) { + return new ProtobufArrayList<E>(toCopy); + } + + protected static <E> ProtobufList<E> emptyProtobufList() { + return ProtobufArrayList.emptyList(); + } + + protected static LazyStringArrayList emptyLazyStringArrayList() { + return LazyStringArrayList.emptyList(); + } } diff --git a/java/src/main/java/com/google/protobuf/IntArrayList.java b/java/src/main/java/com/google/protobuf/IntArrayList.java new file mode 100644 index 00000000..f7609cc9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/IntArrayList.java @@ -0,0 +1,242 @@ +// 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.IntList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link IntList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class IntArrayList extends AbstractProtobufList<Integer> implements IntList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final IntArrayList EMPTY_LIST = new IntArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static IntArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private int[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code IntArrayList}. + */ + IntArrayList() { + array = new int[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}. + */ + IntArrayList(List<Integer> other) { + if (other instanceof IntArrayList) { + IntArrayList list = (IntArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Integer get(int index) { + return getInt(index); + } + + @Override + public int getInt(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Integer set(int index, Integer element) { + return setInt(index, element); + } + + @Override + public int setInt(int index, int element) { + ensureIsMutable(); + ensureIndexInRange(index); + int previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Integer element) { + addInt(index, element); + } + + /** + * Like {@link #add(Integer)} but more efficient in that it doesn't box the element. + */ + @Override + public void addInt(int element) { + addInt(size, element); + } + + /** + * Like {@link #add(int, Integer)} but more efficient in that it doesn't box the element. + */ + private void addInt(int index, int element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + int[] newArray = new int[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection<? extends Integer> collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another IntArrayList to avoid boxing elements. + if (!(collection instanceof IntArrayList)) { + return super.addAll(collection); + } + + IntArrayList list = (IntArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Integer remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + int value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 74bf44c0..20054b79 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -30,6 +30,7 @@ package com.google.protobuf; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.AbstractList; @@ -532,4 +533,132 @@ public class Internal { } } } + + /** + * Extends {@link List} to add the capability to make the list immutable and inspect if it is + * modifiable. + */ + public static interface ProtobufList<E> extends List<E> { + + /** + * Makes this list immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + void makeImmutable(); + + /** + * Returns whether this list can be modified via the publicly accessible {@link List} methods. + */ + boolean isModifiable(); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Integers if + * possible. Does not support null elements. + */ + public static interface IntList extends ProtobufList<Integer> { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + int getInt(int index); + + /** + * Like {@link #add(Integer)} but more efficient in that it doesn't box the element. + */ + void addInt(int element); + + /** + * Like {@link #set(int, Integer)} but more efficient in that it doesn't box the element. + */ + int setInt(int index, int element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Booleans if + * possible. Does not support null elements. + */ + public static interface BooleanList extends ProtobufList<Boolean> { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + boolean getBoolean(int index); + + /** + * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. + */ + void addBoolean(boolean element); + + /** + * Like {@link #set(int, Boolean)} but more efficient in that it doesn't box the element. + */ + boolean setBoolean(int index, boolean element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Longs if + * possible. Does not support null elements. + */ + public static interface LongList extends ProtobufList<Long> { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + long getLong(int index); + + /** + * Like {@link #add(Long)} but more efficient in that it doesn't box the element. + */ + void addLong(long element); + + /** + * Like {@link #set(int, Long)} but more efficient in that it doesn't box the element. + */ + long setLong(int index, long element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Doubles if + * possible. Does not support null elements. + */ + public static interface DoubleList extends ProtobufList<Double> { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + double getDouble(int index); + + /** + * Like {@link #add(Double)} but more efficient in that it doesn't box the element. + */ + void addDouble(double element); + + /** + * Like {@link #set(int, Double)} but more efficient in that it doesn't box the element. + */ + double setDouble(int index, double element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Floats if + * possible. Does not support null elements. + */ + public static interface FloatList extends ProtobufList<Float> { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + float getFloat(int index); + + /** + * Like {@link #add(Float)} but more efficient in that it doesn't box the element. + */ + void addFloat(float element); + + /** + * Like {@link #set(int, Float)} but more efficient in that it doesn't box the element. + */ + float setFloat(int index, float element); + } } diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java index 2d40a51f..a2997e1c 100644 --- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -62,11 +62,20 @@ import java.util.RandomAccess; * * @author jonp@google.com (Jon Perlow) */ -public class LazyStringArrayList extends AbstractList<String> +public class LazyStringArrayList extends AbstractProtobufList<String> implements LazyStringList, RandomAccess { + + private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + static LazyStringArrayList emptyList() { + return EMPTY_LIST; + } - public static final LazyStringList EMPTY = - new LazyStringArrayList().getUnmodifiableView(); + // For compatibility with older runtimes. + public static final LazyStringList EMPTY = EMPTY_LIST; private final List<Object> list; @@ -116,12 +125,26 @@ public class LazyStringArrayList extends AbstractList<String> @Override public String set(int index, String s) { + ensureIsMutable(); Object o = list.set(index, s); return asString(o); } @Override public void add(int index, String element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + private void add(int index, ByteString element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + private void add(int index, byte[] element) { + ensureIsMutable(); list.add(index, element); modCount++; } @@ -137,6 +160,7 @@ public class LazyStringArrayList extends AbstractList<String> @Override public boolean addAll(int index, Collection<? extends String> c) { + ensureIsMutable(); // When copying from another LazyStringList, directly copy the underlying // elements rather than forcing each element to be decoded to a String. Collection<?> collection = c instanceof LazyStringList @@ -148,6 +172,7 @@ public class LazyStringArrayList extends AbstractList<String> // @Override public boolean addAllByteString(Collection<? extends ByteString> values) { + ensureIsMutable(); boolean ret = list.addAll(values); modCount++; return ret; @@ -155,6 +180,7 @@ public class LazyStringArrayList extends AbstractList<String> // @Override public boolean addAllByteArray(Collection<byte[]> c) { + ensureIsMutable(); boolean ret = list.addAll(c); modCount++; return ret; @@ -162,6 +188,7 @@ public class LazyStringArrayList extends AbstractList<String> @Override public String remove(int index) { + ensureIsMutable(); Object o = list.remove(index); modCount++; return asString(o); @@ -169,18 +196,21 @@ public class LazyStringArrayList extends AbstractList<String> @Override public void clear() { + ensureIsMutable(); list.clear(); modCount++; } // @Override public void add(ByteString element) { + ensureIsMutable(); list.add(element); modCount++; } // @Override public void add(byte[] element) { + ensureIsMutable(); list.add(element); modCount++; } @@ -207,14 +237,23 @@ public class LazyStringArrayList extends AbstractList<String> // @Override public void set(int index, ByteString s) { - list.set(index, s); + setAndReturn(index, s); + } + + private Object setAndReturn(int index, ByteString s) { + ensureIsMutable(); + return list.set(index, s); } // @Override public void set(int index, byte[] s) { - list.set(index, s); + setAndReturn(index, s); + } + + private Object setAndReturn(int index, byte[] s) { + ensureIsMutable(); + return list.set(index, s); } - private static String asString(Object o) { if (o instanceof String) { @@ -253,6 +292,7 @@ public class LazyStringArrayList extends AbstractList<String> // @Override public void mergeFrom(LazyStringList other) { + ensureIsMutable(); for (Object o : other.getUnderlyingElements()) { if (o instanceof byte[]) { byte[] b = (byte[]) o; @@ -267,20 +307,15 @@ public class LazyStringArrayList extends AbstractList<String> private static class ByteArrayListView extends AbstractList<byte[]> implements RandomAccess { - private final List<Object> list; + private final LazyStringArrayList list; - ByteArrayListView(List<Object> list) { + ByteArrayListView(LazyStringArrayList list) { this.list = list; } @Override public byte[] get(int index) { - Object o = list.get(index); - byte[] b = asByteArray(o); - if (b != o) { - list.set(index, b); - } - return b; + return list.getByteArray(index); } @Override @@ -290,7 +325,7 @@ public class LazyStringArrayList extends AbstractList<String> @Override public byte[] set(int index, byte[] s) { - Object o = list.set(index, s); + Object o = list.setAndReturn(index, s); modCount++; return asByteArray(o); } @@ -311,25 +346,20 @@ public class LazyStringArrayList extends AbstractList<String> // @Override public List<byte[]> asByteArrayList() { - return new ByteArrayListView(list); + return new ByteArrayListView(this); } private static class ByteStringListView extends AbstractList<ByteString> implements RandomAccess { - private final List<Object> list; + private final LazyStringArrayList list; - ByteStringListView(List<Object> list) { + ByteStringListView(LazyStringArrayList list) { this.list = list; } @Override public ByteString get(int index) { - Object o = list.get(index); - ByteString b = asByteString(o); - if (b != o) { - list.set(index, b); - } - return b; + return list.getByteString(index); } @Override @@ -339,7 +369,7 @@ public class LazyStringArrayList extends AbstractList<String> @Override public ByteString set(int index, ByteString s) { - Object o = list.set(index, s); + Object o = list.setAndReturn(index, s); modCount++; return asByteString(o); } @@ -360,12 +390,15 @@ public class LazyStringArrayList extends AbstractList<String> // @Override public List<ByteString> asByteStringList() { - return new ByteStringListView(list); + return new ByteStringListView(this); } // @Override public LazyStringList getUnmodifiableView() { - return new UnmodifiableLazyStringList(this); + if (isModifiable()) { + return new UnmodifiableLazyStringList(this); + } + return this; } } diff --git a/java/src/main/java/com/google/protobuf/LongArrayList.java b/java/src/main/java/com/google/protobuf/LongArrayList.java new file mode 100644 index 00000000..298617ff --- /dev/null +++ b/java/src/main/java/com/google/protobuf/LongArrayList.java @@ -0,0 +1,242 @@ +// 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.LongList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link LongList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class LongArrayList extends AbstractProtobufList<Long> implements LongList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final LongArrayList EMPTY_LIST = new LongArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static LongArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private long[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code LongArrayList}. + */ + LongArrayList() { + array = new long[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}. + */ + LongArrayList(List<Long> other) { + if (other instanceof LongArrayList) { + LongArrayList list = (LongArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new long[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Long get(int index) { + return getLong(index); + } + + @Override + public long getLong(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Long set(int index, Long element) { + return setLong(index, element); + } + + @Override + public long setLong(int index, long element) { + ensureIsMutable(); + ensureIndexInRange(index); + long previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Long element) { + addLong(index, element); + } + + /** + * Like {@link #add(Long)} but more efficient in that it doesn't box the element. + */ + @Override + public void addLong(long element) { + addLong(size, element); + } + + /** + * Like {@link #add(int, Long)} but more efficient in that it doesn't box the element. + */ + private void addLong(int index, long element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + long[] newArray = new long[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection<? extends Long> collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another LongArrayList to avoid boxing elements. + if (!(collection instanceof LongArrayList)) { + return super.addAll(collection); + } + + LongArrayList list = (LongArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Long remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + long value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/MapField.java b/java/src/main/java/com/google/protobuf/MapField.java index 82906d37..b290993c 100644 --- a/java/src/main/java/com/google/protobuf/MapField.java +++ b/java/src/main/java/com/google/protobuf/MapField.java @@ -30,9 +30,11 @@ package com.google.protobuf; +import com.google.protobuf.MapFieldLite.MutatabilityAwareMap; + import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -51,7 +53,7 @@ import java.util.Map; * and getList() concurrently in multiple threads. If write-access is needed, * all access must be synchronized. */ -public class MapField<K, V> { +public class MapField<K, V> implements MutabilityOracle { /** * Indicates where the data of this map field is currently stored. * @@ -72,8 +74,9 @@ public class MapField<K, V> { */ private enum StorageMode {MAP, LIST, BOTH} + private volatile boolean isMutable; private volatile StorageMode mode; - private Map<K, V> mapData; + private MutatabilityAwareMap<K, V> mapData; private List<Message> listData; // Convert between a map entry Message and a key-value pair. @@ -110,20 +113,19 @@ public class MapField<K, V> { private MapField( Converter<K, V> converter, StorageMode mode, - Map<K, V> mapData, - List<Message> listData) { + Map<K, V> mapData) { this.converter = converter; + this.isMutable = true; this.mode = mode; - this.mapData = mapData; - this.listData = listData; + this.mapData = new MutatabilityAwareMap<K, V>(this, mapData); + this.listData = null; } private MapField( MapEntry<K, V> defaultEntry, StorageMode mode, - Map<K, V> mapData, - List<Message> listData) { - this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData, listData); + Map<K, V> mapData) { + this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData); } @@ -131,14 +133,14 @@ public class MapField<K, V> { public static <K, V> MapField<K, V> emptyMapField( MapEntry<K, V> defaultEntry) { return new MapField<K, V>( - defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap(), null); + defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap()); } /** Creates a new mutable empty MapField. */ public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) { return new MapField<K, V>( - defaultEntry, StorageMode.MAP, new HashMap<K, V>(), null); + defaultEntry, StorageMode.MAP, new LinkedHashMap<K, V>()); } @@ -151,7 +153,7 @@ public class MapField<K, V> { converter.convertMessageToKeyAndValue(message, map); } - private List<Message> convertMapToList(Map<K, V> mapData) { + private List<Message> convertMapToList(MutatabilityAwareMap<K, V> mapData) { List<Message> listData = new ArrayList<Message>(); for (Map.Entry<K, V> entry : mapData.entrySet()) { listData.add( @@ -161,12 +163,12 @@ public class MapField<K, V> { return listData; } - private Map<K, V> convertListToMap(List<Message> listData) { - Map<K, V> mapData = new HashMap<K, V>(); + private MutatabilityAwareMap<K, V> convertListToMap(List<Message> listData) { + Map<K, V> mapData = new LinkedHashMap<K, V>(); for (Message item : listData) { convertMessageToKeyAndValue(item, mapData); } - return mapData; + return new MutatabilityAwareMap<K, V>(this, mapData); } /** Returns the content of this MapField as a read-only Map. */ @@ -199,7 +201,7 @@ public class MapField<K, V> { } public void clear() { - mapData = new HashMap<K, V>(); + mapData = new MutatabilityAwareMap<K, V>(this, new LinkedHashMap<K, V>()); mode = StorageMode.MAP; } @@ -221,7 +223,7 @@ public class MapField<K, V> { /** Returns a deep copy of this MapField. */ public MapField<K, V> copy() { return new MapField<K, V>( - converter, StorageMode.MAP, MapFieldLite.copy(getMap()), null); + converter, StorageMode.MAP, MapFieldLite.copy(getMap())); } /** Gets the content of this MapField as a read-only List. */ @@ -256,4 +258,29 @@ public class MapField<K, V> { Message getMapEntryMessageDefaultInstance() { return converter.getMessageDefaultInstance(); } + + /** + * Makes this list immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + public void makeImmutable() { + isMutable = false; + } + + /** + * Returns whether this field can be modified. + */ + public boolean isMutable() { + return isMutable; + } + + /* (non-Javadoc) + * @see com.google.protobuf.MutabilityOracle#ensureMutable() + */ + @Override + public void ensureMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException(); + } + } } diff --git a/java/src/main/java/com/google/protobuf/MapFieldLite.java b/java/src/main/java/com/google/protobuf/MapFieldLite.java index 7f94c690..c17fa7b1 100644 --- a/java/src/main/java/com/google/protobuf/MapFieldLite.java +++ b/java/src/main/java/com/google/protobuf/MapFieldLite.java @@ -31,9 +31,12 @@ package com.google.protobuf; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; /** * Internal representation of map fields in generated lite-runtime messages. @@ -41,16 +44,21 @@ import java.util.Map; * This class is a protobuf implementation detail. Users shouldn't use this * class directly. */ -public class MapFieldLite<K, V> { - private Map<K, V> mapData; +public class MapFieldLite<K, V> implements MutabilityOracle { + private MutatabilityAwareMap<K, V> mapData; + private boolean isMutable; private MapFieldLite(Map<K, V> mapData) { - this.mapData = mapData; + this.mapData = new MutatabilityAwareMap<K, V>(this, mapData); + this.isMutable = true; } @SuppressWarnings({"rawtypes", "unchecked"}) private static final MapFieldLite EMPTY_MAP_FIELD = new MapFieldLite(Collections.emptyMap()); + static { + EMPTY_MAP_FIELD.makeImmutable(); + } /** Returns an singleton immutable empty MapFieldLite instance. */ @SuppressWarnings({"unchecked", "cast"}) @@ -60,7 +68,7 @@ public class MapFieldLite<K, V> { /** Creates a new MapFieldLite instance. */ public static <K, V> MapFieldLite<K, V> newMapField() { - return new MapFieldLite<K, V>(new HashMap<K, V>()); + return new MapFieldLite<K, V>(new LinkedHashMap<K, V>()); } /** Gets the content of this MapField as a read-only Map. */ @@ -168,7 +176,7 @@ public class MapFieldLite<K, V> { */ @SuppressWarnings("unchecked") static <K, V> Map<K, V> copy(Map<K, V> map) { - Map<K, V> result = new HashMap<K, V>(); + Map<K, V> result = new LinkedHashMap<K, V>(); for (Map.Entry<K, V> entry : map.entrySet()) { result.put(entry.getKey(), (V) copy(entry.getValue())); } @@ -179,4 +187,360 @@ public class MapFieldLite<K, V> { public MapFieldLite<K, V> copy() { return new MapFieldLite<K, V>(copy(mapData)); } + + /** + * Makes this field immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + public void makeImmutable() { + isMutable = false; + } + + /** + * Returns whether this field can be modified. + */ + public boolean isMutable() { + return isMutable; + } + + @Override + public void ensureMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException(); + } + } + + /** + * An internal map that checks for mutability before delegating. + */ + static class MutatabilityAwareMap<K, V> implements Map<K, V> { + private final MutabilityOracle mutabilityOracle; + private final Map<K, V> delegate; + + MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public V get(Object key) { + return delegate.get(key); + } + + @Override + public V put(K key, V value) { + mutabilityOracle.ensureMutable(); + return delegate.put(key, value); + } + + @Override + public V remove(Object key) { + mutabilityOracle.ensureMutable(); + return delegate.remove(key); + } + + @Override + public void putAll(Map<? extends K, ? extends V> m) { + mutabilityOracle.ensureMutable(); + delegate.putAll(m); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public Set<K> keySet() { + return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet()); + } + + @Override + public Collection<V> values() { + return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values()); + } + + @Override + public Set<java.util.Map.Entry<K, V>> entrySet() { + return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet()); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal collection that checks for mutability before delegating. + */ + private static class MutatabilityAwareCollection<E> implements Collection<E> { + private final MutabilityOracle mutabilityOracle; + private final Collection<E> delegate; + + MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public Iterator<E> iterator() { + return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator()); + } + + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + @Override + public <T> T[] toArray(T[] a) { + return delegate.toArray(a); + } + + @Override + public boolean add(E e) { + // Unsupported operation in the delegate. + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + mutabilityOracle.ensureMutable(); + return delegate.remove(o); + } + + @Override + public boolean containsAll(Collection<?> c) { + return delegate.containsAll(c); + } + + @Override + public boolean addAll(Collection<? extends E> c) { + // Unsupported operation in the delegate. + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection<?> c) { + mutabilityOracle.ensureMutable(); + return delegate.removeAll(c); + } + + @Override + public boolean retainAll(Collection<?> c) { + mutabilityOracle.ensureMutable(); + return delegate.retainAll(c); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal set that checks for mutability before delegating. + */ + private static class MutatabilityAwareSet<E> implements Set<E> { + private final MutabilityOracle mutabilityOracle; + private final Set<E> delegate; + + MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public Iterator<E> iterator() { + return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator()); + } + + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + @Override + public <T> T[] toArray(T[] a) { + return delegate.toArray(a); + } + + @Override + public boolean add(E e) { + mutabilityOracle.ensureMutable(); + return delegate.add(e); + } + + @Override + public boolean remove(Object o) { + mutabilityOracle.ensureMutable(); + return delegate.remove(o); + } + + @Override + public boolean containsAll(Collection<?> c) { + return delegate.containsAll(c); + } + + @Override + public boolean addAll(Collection<? extends E> c) { + mutabilityOracle.ensureMutable(); + return delegate.addAll(c); + } + + @Override + public boolean retainAll(Collection<?> c) { + mutabilityOracle.ensureMutable(); + return delegate.retainAll(c); + } + + @Override + public boolean removeAll(Collection<?> c) { + mutabilityOracle.ensureMutable(); + return delegate.removeAll(c); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal iterator that checks for mutability before delegating. + */ + private static class MutatabilityAwareIterator<E> implements Iterator<E> { + private final MutabilityOracle mutabilityOracle; + private final Iterator<E> delegate; + + MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public E next() { + return delegate.next(); + } + + @Override + public void remove() { + mutabilityOracle.ensureMutable(); + delegate.remove(); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } } diff --git a/java/src/main/java/com/google/protobuf/MutabilityOracle.java b/java/src/main/java/com/google/protobuf/MutabilityOracle.java new file mode 100644 index 00000000..82b723c9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MutabilityOracle.java @@ -0,0 +1,48 @@ +// 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; + +/** + * Verifies that an object is mutable, throwing if not. + */ +interface MutabilityOracle { + static final MutabilityOracle IMMUTABLE = new MutabilityOracle() { + @Override + public void ensureMutable() { + throw new UnsupportedOperationException(); + } + }; + + /** + * Throws an {@link UnsupportedOperationException} if not mutable. + */ + void ensureMutable(); +} diff --git a/java/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java new file mode 100644 index 00000000..759368c9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java @@ -0,0 +1,95 @@ +// 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.util.ArrayList; +import java.util.List; + +/** + * Implements {@link ProtobufList} for non-primitive and {@link String} types. + */ +class ProtobufArrayList<E> extends AbstractProtobufList<E> { + + private static final ProtobufArrayList<Object> EMPTY_LIST = new ProtobufArrayList<Object>(); + static { + EMPTY_LIST.makeImmutable(); + } + + @SuppressWarnings("unchecked") // Guaranteed safe by runtime. + public static <E> ProtobufArrayList<E> emptyList() { + return (ProtobufArrayList<E>) EMPTY_LIST; + } + + private final List<E> list; + + ProtobufArrayList() { + list = new ArrayList<E>(); + } + + ProtobufArrayList(List<E> toCopy) { + list = new ArrayList<E>(toCopy); + } + + @Override + public void add(int index, E element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + @Override + public E get(int index) { + return list.get(index); + } + + @Override + public E remove(int index) { + ensureIsMutable(); + E toReturn = list.remove(index); + modCount++; + return toReturn; + } + + @Override + public E set(int index, E element) { + ensureIsMutable(); + E toReturn = list.set(index, element); + modCount++; + return toReturn; + } + + @Override + public int size() { + return list.size(); + } +} diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index dd2b4600..a79ce559 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -1725,7 +1725,7 @@ public final class TextFormat { * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with * "\x") are also recognized. */ - static ByteString unescapeBytes(final CharSequence charString) + public static ByteString unescapeBytes(final CharSequence charString) throws InvalidEscapeSequenceException { // First convert the Java character sequence to UTF-8 bytes. ByteString input = ByteString.copyFromUtf8(charString.toString()); @@ -1808,7 +1808,7 @@ public final class TextFormat { * Thrown by {@link TextFormat#unescapeBytes} and * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. */ - static class InvalidEscapeSequenceException extends IOException { + public static class InvalidEscapeSequenceException extends IOException { private static final long serialVersionUID = -8164033650142593304L; InvalidEscapeSequenceException(final String description) { |