aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/core/src/main/java/com/google
diff options
context:
space:
mode:
authorGravatar Adam Cozzette <acozzette@google.com>2016-06-29 15:23:27 -0700
committerGravatar Adam Cozzette <acozzette@google.com>2016-06-29 15:38:03 -0700
commitd64a2d9941c36a7bc2a7959ea10ab8363192ac14 (patch)
tree52330d146ad63d3d70f3baade00d5d1fea8f5e0c /java/core/src/main/java/com/google
parentc18aa7795a2e02ef700ff8b039d94ecdcc33432f (diff)
Integrated internal changes from Google
This includes all internal changes from around May 20 to now.
Diffstat (limited to 'java/core/src/main/java/com/google')
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractMessage.java95
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java46
-rw-r--r--java/core/src/main/java/com/google/protobuf/BooleanArrayList.java46
-rw-r--r--java/core/src/main/java/com/google/protobuf/CodedOutputStream.java113
-rw-r--r--java/core/src/main/java/com/google/protobuf/Descriptors.java12
-rw-r--r--java/core/src/main/java/com/google/protobuf/DoubleArrayList.java50
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java11
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java95
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java57
-rw-r--r--java/core/src/main/java/com/google/protobuf/FieldSet.java26
-rw-r--r--java/core/src/main/java/com/google/protobuf/FloatArrayList.java51
-rw-r--r--java/core/src/main/java/com/google/protobuf/GeneratedMessage.java202
-rw-r--r--java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java238
-rw-r--r--java/core/src/main/java/com/google/protobuf/IntArrayList.java51
-rw-r--r--java/core/src/main/java/com/google/protobuf/LongArrayList.java53
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapEntry.java219
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapEntryLite.java319
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapField.java413
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapFieldLite.java445
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageReflection.java3
-rw-r--r--java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java708
-rw-r--r--java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java241
-rw-r--r--java/core/src/main/java/com/google/protobuf/TextFormat.java91
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java4
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java17
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnsafeUtil.java210
-rw-r--r--java/core/src/main/java/com/google/protobuf/Utf8.java248
27 files changed, 2728 insertions, 1336 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
index 03c0d579..b8fdb2b2 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
@@ -54,12 +54,40 @@ public abstract class AbstractMessage
// TODO(dweis): Update GeneratedMessage to parameterize with MessageType and BuilderType.
extends AbstractMessageLite
implements Message {
-
+
@Override
public boolean isInitialized() {
return MessageReflection.isInitialized(this);
}
+ /**
+ * Interface for the parent of a Builder that allows the builder to
+ * communicate invalidations back to the parent for use when using nested
+ * builders.
+ */
+ protected interface BuilderParent {
+
+ /**
+ * A builder becomes dirty whenever a field is modified -- including fields
+ * in nested builders -- and becomes clean when build() is called. Thus,
+ * when a builder becomes dirty, all its parents become dirty as well, and
+ * when it becomes clean, all its children become clean. The dirtiness
+ * state is used to invalidate certain cached values.
+ * <br>
+ * To this end, a builder calls markDirty() on its parent whenever it
+ * transitions from clean to dirty. The parent must propagate this call to
+ * its own parent, unless it was already dirty, in which case the
+ * grandparent must necessarily already be dirty as well. The parent can
+ * only transition back to "clean" after calling build() on all children.
+ */
+ void markDirty();
+ }
+
+ /** Create a nested builder. */
+ protected Message.Builder newBuilderForType(BuilderParent parent) {
+ throw new UnsupportedOperationException("Nested builder is not supported for this type.");
+ }
+
@Override
public List<String> findInitializationErrors() {
@@ -460,6 +488,31 @@ public abstract class AbstractMessage
MessageReflection.findMissingFields(message));
}
+ /**
+ * Used to support nested builders and called to mark this builder as clean.
+ * Clean builders will propagate the {@link BuildParent#markDirty()} event
+ * to their parent builders, while dirty builders will not, as their parents
+ * should be dirty already.
+ *
+ * NOTE: Implementations that don't support nested builders don't need to
+ * override this method.
+ */
+ void markClean() {
+ throw new IllegalStateException("Should be overriden by subclasses.");
+ }
+
+ /**
+ * Used to support nested builders and called when this nested builder is
+ * no longer used by its parent builder and should release the reference
+ * to its parent builder.
+ *
+ * NOTE: Implementations that don't support nested builders don't need to
+ * override this method.
+ */
+ void dispose() {
+ throw new IllegalStateException("Should be overriden by subclasses.");
+ }
+
// ===============================================================
// The following definitions seem to be required in order to make javac
// not produce weird errors like:
@@ -550,4 +603,44 @@ public abstract class AbstractMessage
return super.mergeDelimitedFrom(input, extensionRegistry);
}
}
+
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashLong(long n) {
+ return (int) (n ^ (n >>> 32));
+ }
+ //
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashBoolean(boolean b) {
+ return b ? 1231 : 1237;
+ }
+ //
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashEnum(EnumLite e) {
+ return e.getNumber();
+ }
+ //
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatiblity with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashEnumList(List<? extends EnumLite> list) {
+ int hash = 1;
+ for (EnumLite e : list) {
+ hash = 31 * hash + hashEnum(e);
+ }
+ return hash;
+ }
}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
index 43736dd1..046030f3 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
@@ -45,10 +45,10 @@ import java.util.Collection;
*/
public abstract class AbstractMessageLite<
MessageType extends AbstractMessageLite<MessageType, BuilderType>,
- BuilderType extends AbstractMessageLite.Builder<MessageType, BuilderType>>
+ BuilderType extends AbstractMessageLite.Builder<MessageType, BuilderType>>
implements MessageLite {
protected int memoizedHashCode = 0;
-
+
@Override
public ByteString toByteString() {
try {
@@ -57,9 +57,7 @@ public abstract class AbstractMessageLite<
writeTo(out.getCodedOutput());
return out.build();
} catch (IOException e) {
- throw new RuntimeException(
- "Serializing to a ByteString threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getSerializingExceptionMessage("ByteString"), e);
}
}
@@ -72,9 +70,7 @@ public abstract class AbstractMessageLite<
output.checkNoSpaceLeft();
return result;
} catch (IOException e) {
- throw new RuntimeException(
- "Serializing to a byte array threw an IOException " +
- "(should never happen).", e);
+ throw new RuntimeException(getSerializingExceptionMessage("byte array"), e);
}
}
@@ -109,6 +105,11 @@ public abstract class AbstractMessageLite<
return new UninitializedMessageException(this);
}
+ private String getSerializingExceptionMessage(String target) {
+ return "Serializing " + getClass().getName() + " to a " + target
+ + " threw an IOException (should never happen).";
+ }
+
protected static void checkByteStringIsUtf8(ByteString byteString)
throws IllegalArgumentException {
if (!byteString.isValidUtf8()) {
@@ -120,7 +121,7 @@ public abstract class AbstractMessageLite<
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
@@ -156,9 +157,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a ByteString threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
}
}
@@ -174,9 +173,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a ByteString threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
}
}
@@ -197,9 +194,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a byte array threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
}
}
@@ -225,9 +220,7 @@ public abstract class AbstractMessageLite<
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a byte array threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
}
}
@@ -321,7 +314,7 @@ public abstract class AbstractMessageLite<
return mergeDelimitedFrom(input,
ExtensionRegistryLite.getEmptyRegistry());
}
-
+
@Override
@SuppressWarnings("unchecked") // isInstance takes care of this
public BuilderType mergeFrom(final MessageLite other) {
@@ -329,12 +322,17 @@ public abstract class AbstractMessageLite<
throw new IllegalArgumentException(
"mergeFrom(MessageLite) can only merge messages of the same type.");
}
-
+
return internalMergeFrom((MessageType) other);
}
-
+
protected abstract BuilderType internalMergeFrom(MessageType message);
+ private String getReadingExceptionMessage(String target) {
+ return "Reading " + getClass().getName() + " from a " + target
+ + " threw an IOException (should never happen).";
+ }
+
/**
* Construct an UninitializedMessageException reporting missing fields in
* the given message.
diff --git a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
index 8b2820b6..0d9f87ba 100644
--- a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
@@ -38,21 +38,22 @@ 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 {
-
+ extends AbstractProtobufList<Boolean>
+ implements BooleanList, RandomAccess {
+
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.
*/
@@ -72,13 +73,14 @@ final class BooleanArrayList
}
/**
- * Constructs a new mutable {@code BooleanArrayList}.
+ * Constructs a new mutable {@code BooleanArrayList}
+ * containing the same elements as {@code other}.
*/
- private BooleanArrayList(boolean[] array, int size) {
- this.array = array;
+ private BooleanArrayList(boolean[] other, int size) {
+ array = other;
this.size = size;
}
-
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -91,14 +93,14 @@ final class BooleanArrayList
if (size != other.size) {
return false;
}
-
+
final boolean[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
-
+
return true;
}
@@ -170,7 +172,7 @@ final class BooleanArrayList
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);
@@ -178,10 +180,10 @@ final class BooleanArrayList
// 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;
@@ -195,38 +197,38 @@ final class BooleanArrayList
@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();
@@ -255,7 +257,7 @@ final class BooleanArrayList
/**
* 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) {
diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
index ad174d0f..576a350f 100644
--- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -36,12 +36,9 @@ import com.google.protobuf.Utf8.UnpairedSurrogateException;
import java.io.IOException;
import java.io.OutputStream;
-import java.lang.reflect.Field;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -59,9 +56,8 @@ import java.util.logging.Logger;
*/
public abstract class CodedOutputStream extends ByteOutput {
private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
- private static final sun.misc.Unsafe UNSAFE = getUnsafe();
- private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations();
- private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset();
+ private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations();
+ private static final long ARRAY_BASE_OFFSET = UnsafeUtil.getArrayBaseOffset();
private static final int FIXED_32_SIZE = 4;
private static final int FIXED_64_SIZE = 8;
@@ -869,7 +865,7 @@ public abstract class CodedOutputStream extends ByteOutput {
return computeLengthDelimitedFieldSize(value.getSerializedSize());
}
- private static int computeLengthDelimitedFieldSize(int fieldLength) {
+ static int computeLengthDelimitedFieldSize(int fieldLength) {
return computeUInt32SizeNoTag(fieldLength) + fieldLength;
}
@@ -948,6 +944,10 @@ public abstract class CodedOutputStream extends ByteOutput {
OutOfSpaceException(Throwable cause) {
super(MESSAGE, cause);
}
+
+ OutOfSpaceException(String explanationMessage, Throwable cause) {
+ super(MESSAGE + ": " + explanationMessage, cause);
+ }
}
/**
@@ -1250,8 +1250,8 @@ public abstract class CodedOutputStream extends ByteOutput {
try {
buffer[position++] = value;
} catch (IndexOutOfBoundsException e) {
- throw new OutOfSpaceException(new IndexOutOfBoundsException(
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
@@ -1271,11 +1271,11 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = ARRAY_BASE_OFFSET + position;
while (true) {
if ((value & ~0x7F) == 0) {
- UNSAFE.putByte(buffer, pos++, (byte) value);
+ UnsafeUtil.putByte(buffer, pos++, (byte) value);
position++;
return;
} else {
- UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
position++;
value >>>= 7;
}
@@ -1293,8 +1293,7 @@ public abstract class CodedOutputStream extends ByteOutput {
}
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
- new IndexOutOfBoundsException(
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
}
@@ -1308,8 +1307,7 @@ public abstract class CodedOutputStream extends ByteOutput {
buffer[position++] = (byte) ((value >> 24) & 0xFF);
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
- new IndexOutOfBoundsException(
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
@@ -1319,11 +1317,11 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = ARRAY_BASE_OFFSET + position;
while (true) {
if ((value & ~0x7FL) == 0) {
- UNSAFE.putByte(buffer, pos++, (byte) value);
+ UnsafeUtil.putByte(buffer, pos++, (byte) value);
position++;
return;
} else {
- UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
position++;
value >>>= 7;
}
@@ -1341,8 +1339,7 @@ public abstract class CodedOutputStream extends ByteOutput {
}
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
- new IndexOutOfBoundsException(
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
}
@@ -1360,8 +1357,7 @@ public abstract class CodedOutputStream extends ByteOutput {
buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
- new IndexOutOfBoundsException(
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
@@ -1372,8 +1368,7 @@ public abstract class CodedOutputStream extends ByteOutput {
position += length;
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
- new IndexOutOfBoundsException(
- String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
}
}
@@ -1390,8 +1385,7 @@ public abstract class CodedOutputStream extends ByteOutput {
position += length;
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
- new IndexOutOfBoundsException(
- String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
}
}
@@ -1855,10 +1849,10 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = originalPos;
while (true) {
if ((value & ~0x7F) == 0) {
- UNSAFE.putByte(buffer, pos++, (byte) value);
+ UnsafeUtil.putByte(buffer, pos++, (byte) value);
break;
} else {
- UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
value >>>= 7;
}
}
@@ -1890,10 +1884,10 @@ public abstract class CodedOutputStream extends ByteOutput {
long pos = originalPos;
while (true) {
if ((value & ~0x7FL) == 0) {
- UNSAFE.putByte(buffer, pos++, (byte) value);
+ UnsafeUtil.putByte(buffer, pos++, (byte) value);
break;
} else {
- UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
value >>>= 7;
}
}
@@ -2600,65 +2594,4 @@ public abstract class CodedOutputStream extends ByteOutput {
position = 0;
}
}
-
- /**
- * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this
- * platform.
- */
- private static sun.misc.Unsafe getUnsafe() {
- sun.misc.Unsafe unsafe = null;
- try {
- unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<sun.misc.Unsafe>() {
- @Override
- public sun.misc.Unsafe run() throws Exception {
- Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
-
- for (Field f : k.getDeclaredFields()) {
- f.setAccessible(true);
- Object x = f.get(null);
- if (k.isInstance(x)) {
- return k.cast(x);
- }
- }
- // The sun.misc.Unsafe field does not exist.
- return null;
- }
- });
- } catch (Throwable e) {
- // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
- // for Unsafe.
- }
-
- logger.log(Level.FINEST, "sun.misc.Unsafe: {}",
- unsafe != null ? "available" : "unavailable");
- return unsafe;
- }
-
- /**
- * Indicates whether or not unsafe array operations are supported on this platform.
- */
- // TODO(nathanmittler): Add support for Android's MemoryBlock.
- private static boolean supportsUnsafeArrayOperations() {
- boolean supported = false;
- if (UNSAFE != null) {
- try {
- UNSAFE.getClass().getMethod("arrayBaseOffset", Class.class);
- UNSAFE.getClass().getMethod("putByte", Object.class, long.class, byte.class);
- supported = true;
- } catch (Throwable e) {
- // Do nothing.
- }
- }
- logger.log(Level.FINEST, "Unsafe array operations: {}",
- supported ? "available" : "unavailable");
- return supported;
- }
-
- /**
- * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not
- * available.
- */
- private static <T> int byteArrayBaseOffset() {
- return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1;
- }
}
diff --git a/java/core/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java
index e00ea342..1c34c24f 100644
--- a/java/core/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java
@@ -871,6 +871,10 @@ public final class Descriptors {
nestedTypes[i].setProto(proto.getNestedType(i));
}
+ for (int i = 0; i < oneofs.length; i++) {
+ oneofs[i].setProto(proto.getOneofDecl(i));
+ }
+
for (int i = 0; i < enumTypes.length; i++) {
enumTypes[i].setProto(proto.getEnumType(i));
}
@@ -2513,6 +2517,10 @@ public final class Descriptors {
public int getFieldCount() { return fieldCount; }
+ public OneofOptions getOptions() {
+ return proto.getOptions();
+ }
+
/** Get a list of this message type's fields. */
public List<FieldDescriptor> getFields() {
return Collections.unmodifiableList(Arrays.asList(fields));
@@ -2522,6 +2530,10 @@ public final class Descriptors {
return fields[index];
}
+ private void setProto(final OneofDescriptorProto proto) {
+ this.proto = proto;
+ }
+
private OneofDescriptor(final OneofDescriptorProto proto,
final FileDescriptor file,
final Descriptor parent,
diff --git a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
index a9543b83..6177f3ca 100644
--- a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
@@ -38,26 +38,27 @@ 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 {
-
+ extends AbstractProtobufList<Double>
+ implements DoubleList, RandomAccess {
+
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.
@@ -72,13 +73,14 @@ final class DoubleArrayList
}
/**
- * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}.
+ * Constructs a new mutable {@code DoubleArrayList}
+ * containing the same elements as {@code other}.
*/
- private DoubleArrayList(double[] array, int size) {
- this.array = array;
+ private DoubleArrayList(double[] other, int size) {
+ array = other;
this.size = size;
}
-
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -91,14 +93,14 @@ final class DoubleArrayList
if (size != other.size) {
return false;
}
-
+
final double[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
-
+
return true;
}
@@ -119,7 +121,7 @@ final class DoubleArrayList
}
return new DoubleArrayList(Arrays.copyOf(array, capacity), size);
}
-
+
@Override
public Double get(int index) {
return getDouble(index);
@@ -171,7 +173,7 @@ final class DoubleArrayList
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);
@@ -179,10 +181,10 @@ final class DoubleArrayList
// 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;
@@ -196,38 +198,38 @@ final class DoubleArrayList
@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();
@@ -256,7 +258,7 @@ final class DoubleArrayList
/**
* 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) {
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
index 0067392f..1c2e7e6f 100644
--- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
@@ -101,7 +101,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
/** Get the unmodifiable singleton empty instance. */
public static ExtensionRegistry getEmptyRegistry() {
- return EMPTY;
+ return EMPTY_REGISTRY;
}
@@ -243,6 +243,11 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
add(newExtensionInfo(extension), extension.getExtensionType());
}
+ /** Add an extension from a generated file to the registry. */
+ public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
+ add((Extension<?, ?>) extension);
+ }
+
static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) {
if (extension.getDescriptor().getJavaType() ==
FieldDescriptor.JavaType.MESSAGE) {
@@ -311,7 +316,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
ExtensionRegistry(boolean empty) {
- super(ExtensionRegistryLite.getEmptyRegistry());
+ super(EMPTY_REGISTRY_LITE);
this.immutableExtensionsByName =
Collections.<String, ExtensionInfo>emptyMap();
this.mutableExtensionsByName =
@@ -321,7 +326,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
this.mutableExtensionsByNumber =
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
}
- private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
+ static final ExtensionRegistry EMPTY_REGISTRY = new ExtensionRegistry(true);
private void add(
final ExtensionInfo extension,
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java
new file mode 100644
index 00000000..23174e24
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.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 static com.google.protobuf.ExtensionRegistryLite.EMPTY_REGISTRY_LITE;
+
+/**
+ * A factory object to create instances of {@link ExtensionRegistryLite}.
+ *
+ * <p>
+ * This factory detects (via reflection) if the full (non-Lite) protocol buffer libraries
+ * are available, and if so, the instances returned are actually {@link ExtensionRegistry}.
+ */
+final class ExtensionRegistryFactory {
+
+ static final String FULL_REGISTRY_CLASS_NAME = "com.google.protobuf.ExtensionRegistry";
+
+ /* Visible for Testing
+ @Nullable */
+ static final Class<?> EXTENSION_REGISTRY_CLASS = reflectExtensionRegistry();
+
+ /* @Nullable */
+ static Class<?> reflectExtensionRegistry() {
+ try {
+ return Class.forName(FULL_REGISTRY_CLASS_NAME);
+ } catch (ClassNotFoundException e) {
+ // The exception allocation is potentially expensive on Android (where it can be triggered
+ // many times at start up). Is there a way to ameliorate this?
+ return null;
+ }
+ }
+
+ /** Construct a new, empty instance. */
+ public static ExtensionRegistryLite create() {
+ if (EXTENSION_REGISTRY_CLASS != null) {
+ try {
+ return invokeSubclassFactory("newInstance");
+ } catch (Exception e) {
+ // return a Lite registry.
+ }
+ }
+ return new ExtensionRegistryLite();
+ }
+
+ /** Get the unmodifiable singleton empty instance. */
+ public static ExtensionRegistryLite createEmpty() {
+ if (EXTENSION_REGISTRY_CLASS != null) {
+ try {
+ return invokeSubclassFactory("getEmptyRegistry");
+ } catch (Exception e) {
+ // return a Lite registry.
+ }
+ }
+ return EMPTY_REGISTRY_LITE;
+ }
+
+ static boolean isFullRegistry(ExtensionRegistryLite registry) {
+ return EXTENSION_REGISTRY_CLASS != null
+ && EXTENSION_REGISTRY_CLASS.isAssignableFrom(registry.getClass());
+ }
+
+ private static final ExtensionRegistryLite invokeSubclassFactory(String methodName)
+ throws Exception {
+ return (ExtensionRegistryLite) EXTENSION_REGISTRY_CLASS
+ .getMethod(methodName).invoke(null);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
index 65cf7385..5e4d7739 100644
--- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
@@ -79,6 +79,22 @@ public class ExtensionRegistryLite {
// applications. Need to support this feature on smaller granularity.
private static volatile boolean eagerlyParseMessageSets = false;
+ // Visible for testing.
+ static final String EXTENSION_CLASS_NAME = "com.google.protobuf.Extension";
+
+ /* @Nullable */
+ static Class<?> resolveExtensionClass() {
+ try {
+ return Class.forName(EXTENSION_CLASS_NAME);
+ } catch (ClassNotFoundException e) {
+ // See comment in ExtensionRegistryFactory on the potential expense of this.
+ return null;
+ }
+ }
+
+ /* @Nullable */
+ private static final Class<?> extensionClass = resolveExtensionClass();
+
public static boolean isEagerlyParseMessageSets() {
return eagerlyParseMessageSets;
}
@@ -87,14 +103,22 @@ public class ExtensionRegistryLite {
eagerlyParseMessageSets = isEagerlyParse;
}
- /** Construct a new, empty instance. */
+ /**
+ * Construct a new, empty instance.
+ *
+ * <p>
+ * This may be an {@code ExtensionRegistry} if the full (non-Lite) proto libraries are available.
+ */
public static ExtensionRegistryLite newInstance() {
- return new ExtensionRegistryLite();
+ return ExtensionRegistryFactory.create();
}
- /** Get the unmodifiable singleton empty instance. */
+ /**
+ * Get the unmodifiable singleton empty instance of either ExtensionRegistryLite or
+ * {@code ExtensionRegistry} (if the full (non-Lite) proto libraries are available).
+ */
public static ExtensionRegistryLite getEmptyRegistry() {
- return EMPTY;
+ return ExtensionRegistryFactory.createEmpty();
}
/** Returns an unmodifiable view of the registry. */
@@ -128,6 +152,23 @@ public class ExtensionRegistryLite {
extension);
}
+ /**
+ * Add an extension from a lite generated file to the registry only if it is
+ * a non-lite extension i.e. {@link GeneratedMessageLite.GeneratedExtension}. */
+ public final void add(ExtensionLite<?, ?> extension) {
+ if (GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(extension.getClass())) {
+ add((GeneratedMessageLite.GeneratedExtension<?, ?>) extension);
+ }
+ if (ExtensionRegistryFactory.isFullRegistry(this)) {
+ try {
+ this.getClass().getMethod("add", extensionClass).invoke(this, extension);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ String.format("Could not invoke ExtensionRegistry#add for %s", extension), e);
+ }
+ }
+ }
+
// =================================================================
// Private stuff.
@@ -139,9 +180,11 @@ public class ExtensionRegistryLite {
new HashMap<ObjectIntPair,
GeneratedMessageLite.GeneratedExtension<?, ?>>();
}
+ static final ExtensionRegistryLite EMPTY_REGISTRY_LITE =
+ new ExtensionRegistryLite(true);
ExtensionRegistryLite(ExtensionRegistryLite other) {
- if (other == EMPTY) {
+ if (other == EMPTY_REGISTRY_LITE) {
this.extensionsByNumber = Collections.emptyMap();
} else {
this.extensionsByNumber =
@@ -153,11 +196,9 @@ public class ExtensionRegistryLite {
GeneratedMessageLite.GeneratedExtension<?, ?>>
extensionsByNumber;
- private ExtensionRegistryLite(boolean empty) {
+ ExtensionRegistryLite(boolean empty) {
this.extensionsByNumber = Collections.emptyMap();
}
- private static final ExtensionRegistryLite EMPTY =
- new ExtensionRegistryLite(true);
/** A (Object, int) pair, used as a map key. */
private static final class ObjectIntPair {
diff --git a/java/core/src/main/java/com/google/protobuf/FieldSet.java b/java/core/src/main/java/com/google/protobuf/FieldSet.java
index 4e89709f..5b251743 100644
--- a/java/core/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java
@@ -120,21 +120,21 @@ final class FieldSet<FieldDescriptorType extends
public boolean isImmutable() {
return isImmutable;
}
-
+
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
-
+
if (!(o instanceof FieldSet)) {
return false;
}
-
+
FieldSet<?> other = (FieldSet<?>) o;
- return other.fields.equals(other.fields);
+ return fields.equals(other.fields);
}
-
+
@Override
public int hashCode() {
return fields.hashCode();
@@ -493,7 +493,7 @@ final class FieldSet<FieldDescriptorType extends
}
/**
- * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
+ * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
* {@link FieldSet}.
*/
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
@@ -638,10 +638,11 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
- private static void writeElement(final CodedOutputStream output,
- final WireFormat.FieldType type,
- final int number,
- final Object value) throws IOException {
+ static void writeElement(
+ final CodedOutputStream output,
+ final WireFormat.FieldType type,
+ final int number,
+ final Object value) throws IOException {
// Special case for groups, which need a start and end tag; other fields
// can just use writeTag() and writeFieldNoTag().
if (type == WireFormat.FieldType.GROUP) {
@@ -804,9 +805,8 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
- private static int computeElementSize(
- final WireFormat.FieldType type,
- final int number, final Object value) {
+ static int computeElementSize(
+ final WireFormat.FieldType type, final int number, final Object value) {
int tagSize = CodedOutputStream.computeTagSize(number);
if (type == WireFormat.FieldType.GROUP) {
// Only count the end group tag for proto2 messages as for proto1 the end
diff --git a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
index 63cb6d77..90d6154b 100644
--- a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
@@ -38,25 +38,27 @@ 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 {
-
+final class FloatArrayList
+ extends AbstractProtobufList<Float>
+ implements FloatList, RandomAccess {
+
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.
@@ -71,13 +73,14 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
}
/**
- * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}.
+ * Constructs a new mutable {@code FloatArrayList}
+ * containing the same elements as {@code other}.
*/
- private FloatArrayList(float[] array, int size) {
- this.array = array;
+ private FloatArrayList(float[] other, int size) {
+ array = other;
this.size = size;
}
-
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -90,14 +93,14 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
if (size != other.size) {
return false;
}
-
+
final float[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
-
+
return true;
}
@@ -117,7 +120,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
}
return new FloatArrayList(Arrays.copyOf(array, capacity), size);
}
-
+
@Override
public Float get(int index) {
return getFloat(index);
@@ -169,7 +172,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
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);
@@ -177,10 +180,10 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
// 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;
@@ -194,38 +197,38 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
@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();
@@ -254,7 +257,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
/**
* 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) {
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
index 790cb622..2c87302b 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -355,31 +355,30 @@ public abstract class GeneratedMessage extends AbstractMessage
// Noop for messages without extensions.
}
- protected abstract Message.Builder newBuilderForType(BuilderParent parent);
+ /**
+ * TODO(xiaofeng): remove this after b/29368482 is fixed. We need to move this
+ * interface to AbstractMessage in order to versioning GeneratedMessage but
+ * this move breaks binary compatibility for AppEngine. After AppEngine is
+ * fixed we can exlude this from google3.
+ */
+ protected interface BuilderParent extends AbstractMessage.BuilderParent {}
/**
- * Interface for the parent of a Builder that allows the builder to
- * communicate invalidations back to the parent for use when using nested
- * builders.
+ * TODO(xiaofeng): remove this together with GeneratedMessage.BuilderParent.
*/
- protected interface BuilderParent {
+ protected abstract Message.Builder newBuilderForType(BuilderParent parent);
- /**
- * A builder becomes dirty whenever a field is modified -- including fields
- * in nested builders -- and becomes clean when build() is called. Thus,
- * when a builder becomes dirty, all its parents become dirty as well, and
- * when it becomes clean, all its children become clean. The dirtiness
- * state is used to invalidate certain cached values.
- * <br>
- * To this end, a builder calls markAsDirty() on its parent whenever it
- * transitions from clean to dirty. The parent must propagate this call to
- * its own parent, unless it was already dirty, in which case the
- * grandparent must necessarily already be dirty as well. The parent can
- * only transition back to "clean" after calling build() on all children.
- */
- void markDirty();
+ @Override
+ protected Message.Builder newBuilderForType(final AbstractMessage.BuilderParent parent) {
+ return newBuilderForType(new BuilderParent() {
+ @Override
+ public void markDirty() {
+ parent.markDirty();
+ }
+ });
}
+
@SuppressWarnings("unchecked")
public abstract static class Builder <BuilderType extends Builder<BuilderType>>
extends AbstractMessage.Builder<BuilderType> {
@@ -403,6 +402,7 @@ public abstract class GeneratedMessage extends AbstractMessage
this.builderParent = builderParent;
}
+ @Override
void dispose() {
builderParent = null;
}
@@ -420,6 +420,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* Called by the subclass or a builder to notify us that a message was
* built and may be cached and therefore invalidations are needed.
*/
+ @Override
protected void markClean() {
this.isClean = true;
}
@@ -755,6 +756,33 @@ public abstract class GeneratedMessage extends AbstractMessage
<Type> Type getExtension(
ExtensionLite<MessageType, List<Type>> extension,
int index);
+
+ /** Check if a singular extension is present. */
+ <Type> boolean hasExtension(
+ Extension<MessageType, Type> extension);
+ /** Check if a singular extension is present. */
+ <Type> boolean hasExtension(
+ GeneratedExtension<MessageType, Type> extension);
+ /** Get the number of elements in a repeated extension. */
+ <Type> int getExtensionCount(
+ Extension<MessageType, List<Type>> extension);
+ /** Get the number of elements in a repeated extension. */
+ <Type> int getExtensionCount(
+ GeneratedExtension<MessageType, List<Type>> extension);
+ /** Get the value of an extension. */
+ <Type> Type getExtension(
+ Extension<MessageType, Type> extension);
+ /** Get the value of an extension. */
+ <Type> Type getExtension(
+ GeneratedExtension<MessageType, Type> extension);
+ /** Get one element of a repeated extension. */
+ <Type> Type getExtension(
+ Extension<MessageType, List<Type>> extension,
+ int index);
+ /** Get one element of a repeated extension. */
+ <Type> Type getExtension(
+ GeneratedExtension<MessageType, List<Type>> extension,
+ int index);
}
/**
@@ -881,6 +909,53 @@ public abstract class GeneratedMessage extends AbstractMessage
extensions.getRepeatedField(descriptor, index));
}
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final Extension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final GeneratedExtension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get one element of a repeated extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final Extension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Get one element of a repeated extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+
/** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() {
return extensions.isInitialized();
@@ -1269,6 +1344,95 @@ public abstract class GeneratedMessage extends AbstractMessage
return (BuilderType) this;
}
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final Extension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final GeneratedExtension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final Extension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Set the value of an extension. */
+ public final <Type> BuilderType setExtension(
+ final Extension<MessageType, Type> extension, final Type value) {
+ return setExtension((ExtensionLite<MessageType, Type>) extension, value);
+ }
+ /** Set the value of an extension. */
+ public final <Type> BuilderType setExtension(
+ final GeneratedExtension<MessageType, Type> extension, final Type value) {
+ return setExtension((ExtensionLite<MessageType, Type>) extension, value);
+ }
+ /** Set the value of one element of a repeated extension. */
+ public final <Type> BuilderType setExtension(
+ final Extension<MessageType, List<Type>> extension,
+ final int index, final Type value) {
+ return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
+ }
+ /** Set the value of one element of a repeated extension. */
+ public final <Type> BuilderType setExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension,
+ final int index, final Type value) {
+ return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
+ }
+ /** Append a value to a repeated extension. */
+ public final <Type> BuilderType addExtension(
+ final Extension<MessageType, List<Type>> extension, final Type value) {
+ return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
+ }
+ /** Append a value to a repeated extension. */
+ public final <Type> BuilderType addExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final Type value) {
+ return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
+ }
+ /** Clear an extension. */
+ public final <Type> BuilderType clearExtension(
+ final Extension<MessageType, ?> extension) {
+ return clearExtension((ExtensionLite<MessageType, ?>) extension);
+ }
+ /** Clear an extension. */
+ public final <Type> BuilderType clearExtension(
+ final GeneratedExtension<MessageType, ?> extension) {
+ return clearExtension((ExtensionLite<MessageType, ?>) extension);
+ }
+
/** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() {
return extensions.isInitialized();
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index c5adc5ad..214971b1 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -59,15 +59,15 @@ import java.util.Map;
*/
public abstract class GeneratedMessageLite<
MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
- BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
+ BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
extends AbstractMessageLite<MessageType, BuilderType> {
/** For use by generated code only. Lazily initialized to reduce allocations. */
protected UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance();
-
+
/** For use by generated code only. */
protected int memoizedSerializedSize = -1;
-
+
@Override
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final Parser<MessageType> getParserForType() {
@@ -113,7 +113,7 @@ public abstract class GeneratedMessageLite<
}
return memoizedHashCode;
}
-
+
@SuppressWarnings("unchecked") // Guaranteed by runtime
int hashCode(HashCodeVisitor visitor) {
if (memoizedHashCode == 0) {
@@ -125,18 +125,18 @@ public abstract class GeneratedMessageLite<
}
return memoizedHashCode;
}
-
+
@SuppressWarnings("unchecked") // Guaranteed by isInstance + runtime
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
-
+
if (!getDefaultInstanceForType().getClass().isInstance(other)) {
return false;
}
-
+
try {
visit(EqualsVisitor.INSTANCE, (MessageType) other);
} catch (NotEqualsException e) {
@@ -144,7 +144,7 @@ public abstract class GeneratedMessageLite<
}
return true;
}
-
+
/**
* Same as {@link #equals(Object)} but throws {@code NotEqualsException}.
*/
@@ -153,7 +153,7 @@ public abstract class GeneratedMessageLite<
if (this == other) {
return true;
}
-
+
if (!getDefaultInstanceForType().getClass().isInstance(other)) {
return false;
}
@@ -161,7 +161,7 @@ public abstract class GeneratedMessageLite<
visit(visitor, (MessageType) other);
return true;
}
-
+
// The general strategy for unknown fields is to use an UnknownFieldSetLite that is treated as
// mutable during the parsing constructor and immutable after. This allows us to avoid
// any unnecessary intermediary allocations while reducing the generated code size.
@@ -174,10 +174,10 @@ public abstract class GeneratedMessageLite<
unknownFields = UnknownFieldSetLite.newInstance();
}
}
-
+
/**
* Called by subclasses to parse an unknown field. For use by generated code only.
- *
+ *
* @return {@code true} unless the tag is an end-group tag.
*/
protected boolean parseUnknownField(int tag, CodedInputStream input) throws IOException {
@@ -185,7 +185,7 @@ public abstract class GeneratedMessageLite<
if (WireFormat.getTagWireType(tag) == WireFormat.WIRETYPE_END_GROUP) {
return false;
}
-
+
ensureUnknownFieldsInitialized();
return unknownFields.mergeFieldFrom(tag, input);
}
@@ -197,7 +197,7 @@ public abstract class GeneratedMessageLite<
ensureUnknownFieldsInitialized();
unknownFields.mergeVarintField(tag, value);
}
-
+
/**
* Called by subclasses to parse an unknown field. For use by generated code only.
*/
@@ -205,7 +205,7 @@ public abstract class GeneratedMessageLite<
ensureUnknownFieldsInitialized();
unknownFields.mergeLengthDelimitedField(fieldNumber, value);
}
-
+
/**
* Called by subclasses to complete parsing. For use by generated code only.
*/
@@ -292,7 +292,7 @@ public abstract class GeneratedMessageLite<
dynamicMethod(MethodToInvoke.VISIT, visitor, other);
unknownFields = visitor.visitUnknownFields(unknownFields, other.unknownFields);
}
-
+
/**
* Merge some unknown fields into the {@link UnknownFieldSetLite} for this
* message.
@@ -359,9 +359,9 @@ public abstract class GeneratedMessageLite<
if (isBuilt) {
return instance;
}
-
+
instance.makeImmutable();
-
+
isBuilt = true;
return instance;
}
@@ -374,24 +374,24 @@ public abstract class GeneratedMessageLite<
}
return result;
}
-
+
@Override
protected BuilderType internalMergeFrom(MessageType message) {
return mergeFrom(message);
}
-
+
/** All subclasses implement this. */
public BuilderType mergeFrom(MessageType message) {
copyOnWrite();
instance.visit(MergeFromVisitor.INSTANCE, message);
return (BuilderType) this;
}
-
+
@Override
public MessageType getDefaultInstanceForType() {
return defaultInstance;
}
-
+
@Override
public BuilderType mergeFrom(
com.google.protobuf.CodedInputStream input,
@@ -466,12 +466,12 @@ public abstract class GeneratedMessageLite<
super.visit(visitor, other);
extensions = visitor.visitExtensions(extensions, other.extensions);
}
-
+
/**
* Parse an unknown field or an extension. For use by generated code only.
- *
+ *
* <p>For use by generated code only.
- *
+ *
* @return {@code true} unless the tag is an end-group tag.
*/
protected <MessageType extends MessageLite> boolean parseUnknownField(
@@ -590,7 +590,7 @@ public abstract class GeneratedMessageLite<
return true;
}
-
+
private void verifyExtensionContainingType(
final GeneratedExtension<MessageType, ?> extension) {
if (extension.getContainingTypeDefaultInstance() !=
@@ -607,7 +607,7 @@ public abstract class GeneratedMessageLite<
public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extension) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
return extensions.hasField(extensionLite.descriptor);
}
@@ -618,7 +618,7 @@ public abstract class GeneratedMessageLite<
final ExtensionLite<MessageType, List<Type>> extension) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
return extensions.getRepeatedFieldCount(extensionLite.descriptor);
}
@@ -629,7 +629,7 @@ public abstract class GeneratedMessageLite<
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) {
@@ -660,7 +660,7 @@ public abstract class GeneratedMessageLite<
@Override
protected final void makeImmutable() {
super.makeImmutable();
-
+
extensions.makeImmutable();
}
@@ -734,7 +734,7 @@ public abstract class GeneratedMessageLite<
implements ExtendableMessageOrBuilder<MessageType, BuilderType> {
protected ExtendableBuilder(MessageType defaultInstance) {
super(defaultInstance);
-
+
// 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
@@ -753,7 +753,7 @@ public abstract class GeneratedMessageLite<
if (!isBuilt) {
return;
}
-
+
super.copyOnWrite();
instance.extensions = instance.extensions.clone();
}
@@ -814,14 +814,14 @@ public abstract class GeneratedMessageLite<
public BuilderType clone() {
return super.clone();
}
-
+
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
final ExtensionLite<MessageType, Type> extension,
final Type value) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value));
@@ -834,7 +834,7 @@ public abstract class GeneratedMessageLite<
final int index, final Type value) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.setRepeatedField(
@@ -848,7 +848,7 @@ public abstract class GeneratedMessageLite<
final Type value) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.addRepeatedField(
@@ -860,7 +860,7 @@ public abstract class GeneratedMessageLite<
public final <Type> BuilderType clearExtension(
final ExtensionLite<MessageType, ?> extension) {
GeneratedExtension<MessageType, ?> extensionLite = checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
instance.extensions.clearField(extensionLite.descriptor);
@@ -1157,7 +1157,7 @@ public abstract class GeneratedMessageLite<
public static SerializedForm of(MessageLite message) {
return new SerializedForm(message);
}
-
+
private static final long serialVersionUID = 0L;
private final String messageClassName;
@@ -1191,7 +1191,7 @@ public abstract class GeneratedMessageLite<
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
} catch (NoSuchFieldException e) {
- throw new RuntimeException("Unable to find DEFAULT_INSTANCE in " + messageClassName, e);
+ return readResolveFallback();
} catch (SecurityException e) {
throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e);
} catch (IllegalAccessException e) {
@@ -1200,8 +1200,35 @@ public abstract class GeneratedMessageLite<
throw new RuntimeException("Unable to understand proto buffer", e);
}
}
+
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 generated code.
+ */
+ @Deprecated
+ private Object readResolveFallback() throws ObjectStreamException {
+ try {
+ Class<?> messageClass = Class.forName(messageClassName);
+ java.lang.reflect.Field defaultInstanceField =
+ messageClass.getDeclaredField("defaultInstance");
+ defaultInstanceField.setAccessible(true);
+ MessageLite defaultInstance = (MessageLite) defaultInstanceField.get(null);
+ return defaultInstance.newBuilderForType()
+ .mergeFrom(asBytes)
+ .buildPartial();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Unable to find defaultInstance in " + messageClassName, e);
+ } catch (SecurityException e) {
+ throw new RuntimeException("Unable to call defaultInstance in " + messageClassName, e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Unable to call parsePartialFrom", e);
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException("Unable to understand proto buffer", e);
+ }
+ }
}
-
+
/**
* Checks that the {@link Extension} is Lite and returns it as a
* {@link GeneratedExtension}.
@@ -1215,7 +1242,7 @@ public abstract class GeneratedMessageLite<
if (!extension.isLite()) {
throw new IllegalArgumentException("Expected a lite extension.");
}
-
+
return (GeneratedExtension<MessageType, T>) extension;
}
@@ -1227,8 +1254,8 @@ public abstract class GeneratedMessageLite<
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);
}
@@ -1246,7 +1273,7 @@ public abstract class GeneratedMessageLite<
protected static LongList emptyLongList() {
return LongArrayList.emptyList();
}
-
+
protected static LongList mutableCopy(LongList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@@ -1256,7 +1283,7 @@ public abstract class GeneratedMessageLite<
protected static FloatList emptyFloatList() {
return FloatArrayList.emptyList();
}
-
+
protected static FloatList mutableCopy(FloatList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@@ -1266,7 +1293,7 @@ public abstract class GeneratedMessageLite<
protected static DoubleList emptyDoubleList() {
return DoubleArrayList.emptyList();
}
-
+
protected static DoubleList mutableCopy(DoubleList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@@ -1276,7 +1303,7 @@ public abstract class GeneratedMessageLite<
protected static BooleanList emptyBooleanList() {
return BooleanArrayList.emptyList();
}
-
+
protected static BooleanList mutableCopy(BooleanList list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@@ -1286,7 +1313,7 @@ public abstract class GeneratedMessageLite<
protected static <E> ProtobufList<E> emptyProtobufList() {
return ProtobufArrayList.emptyList();
}
-
+
protected static <E> ProtobufList<E> mutableCopy(ProtobufList<E> list) {
int size = list.size();
return list.mutableCopyWithCapacity(
@@ -1300,20 +1327,20 @@ public abstract class GeneratedMessageLite<
*/
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);
}
}
-
+
/**
* A static helper method for parsing a partial from input using the extension registry and the
* instance.
@@ -1335,14 +1362,14 @@ public abstract class GeneratedMessageLite<
}
return result;
}
-
+
protected static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
T defaultInstance,
CodedInputStream input)
throws InvalidProtocolBufferException {
return parsePartialFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry());
}
-
+
/**
* Helper method to check if message is initialized.
*
@@ -1373,7 +1400,7 @@ public abstract class GeneratedMessageLite<
throws InvalidProtocolBufferException {
return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry));
}
-
+
// This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
// ByteString.
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
@@ -1393,7 +1420,7 @@ public abstract class GeneratedMessageLite<
throw e;
}
}
-
+
// This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
// ByteString.
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
@@ -1477,7 +1504,7 @@ public abstract class GeneratedMessageLite<
return checkMessageInitialized(
parsePartialDelimitedFrom(defaultInstance, input, extensionRegistry));
}
-
+
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialDelimitedFrom(
T defaultInstance,
InputStream input,
@@ -1530,13 +1557,12 @@ public abstract class GeneratedMessageLite<
Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other);
Object visitOneofMessage(boolean minePresent, Object mine, Object other);
void visitOneofNotSet(boolean minePresent);
-
+
/**
* Message fields use null sentinals.
*/
<T extends MessageLite> T visitMessage(T mine, T other);
- LazyFieldLite visitLazyMessage(
- boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other);
+ LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other);
<T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other);
BooleanList visitBooleanList(BooleanList mine, BooleanList other);
@@ -1686,7 +1712,7 @@ public abstract class GeneratedMessageLite<
}
throw NOT_EQUALS;
}
-
+
@Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
if (minePresent && ((GeneratedMessageLite<?, ?>) mine).equals(this, (MessageLite) other)) {
@@ -1694,7 +1720,7 @@ public abstract class GeneratedMessageLite<
}
throw NOT_EQUALS;
}
-
+
@Override
public void visitOneofNotSet(boolean minePresent) {
if (minePresent) {
@@ -1716,13 +1742,17 @@ public abstract class GeneratedMessageLite<
return mine;
}
-
+
@Override
public LazyFieldLite visitLazyMessage(
- boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) {
- if (!minePresent && !otherPresent) {
- return mine;
- } else if (minePresent && otherPresent && mine.equals(other)) {
+ LazyFieldLite mine, LazyFieldLite other) {
+ if (mine == null && other == null) {
+ return null;
+ }
+ if (mine == null || other == null) {
+ throw NOT_EQUALS;
+ }
+ if (mine.equals(other)) {
return mine;
}
throw NOT_EQUALS;
@@ -1813,7 +1843,7 @@ public abstract class GeneratedMessageLite<
// The caller must ensure that the visitor is invoked parameterized with this and this such that
// other is this. This is required due to how oneof cases are handled. See the class comment
// on Visitor for more information.
-
+
private int hashCode = 0;
@Override
@@ -1909,7 +1939,7 @@ public abstract class GeneratedMessageLite<
hashCode = (53 * hashCode) + mine.hashCode();
return mine;
}
-
+
@Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
return visitMessage((MessageLite) mine, (MessageLite) other);
@@ -1918,7 +1948,7 @@ public abstract class GeneratedMessageLite<
@Override
public void visitOneofNotSet(boolean minePresent) {
if (minePresent) {
- throw new IllegalStateException(); // Can't happen if other == this.
+ throw new IllegalStateException(); // Can't happen if other == this.
}
}
@@ -1939,9 +1969,14 @@ public abstract class GeneratedMessageLite<
}
@Override
- public LazyFieldLite visitLazyMessage(
- boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) {
- hashCode = (53 * hashCode) + mine.hashCode();
+ public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
+ final int protoHash;
+ if (mine != null) {
+ protoHash = mine.hashCode();
+ } else {
+ protoHash = 37;
+ }
+ hashCode = (53 * hashCode) + protoHash;
return mine;
}
@@ -1996,7 +2031,7 @@ public abstract class GeneratedMessageLite<
hashCode = (53 * hashCode) + mine.hashCode();
return mine;
}
-
+
@Override
public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
hashCode = (53 * hashCode) + mine.hashCode();
@@ -2064,7 +2099,7 @@ public abstract class GeneratedMessageLite<
@Override
public Object visitOneofDouble(boolean minePresent, Object mine, Object other) {
- return other;
+ return other;
}
@Override
@@ -2074,29 +2109,26 @@ public abstract class GeneratedMessageLite<
@Override
public Object visitOneofLong(boolean minePresent, Object mine, Object other) {
- return other;
+ return other;
}
@Override
public Object visitOneofString(boolean minePresent, Object mine, Object other) {
- return other;
+ return other;
}
@Override
public Object visitOneofByteString(boolean minePresent, Object mine, Object other) {
- return other;
+ return other;
}
@Override
public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) {
- if (minePresent) {
- LazyFieldLite lazy = (LazyFieldLite) mine;
- lazy.merge((LazyFieldLite) other);
- return lazy;
- }
- return other;
+ LazyFieldLite lazy = minePresent ? (LazyFieldLite) mine : new LazyFieldLite();
+ lazy.merge((LazyFieldLite) other);
+ return lazy;
}
-
+
@Override
public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
if (minePresent) {
@@ -2104,7 +2136,7 @@ public abstract class GeneratedMessageLite<
}
return other;
}
-
+
@Override
public void visitOneofNotSet(boolean minePresent) {
return;
@@ -2121,12 +2153,13 @@ public abstract class GeneratedMessageLite<
}
@Override
- public LazyFieldLite visitLazyMessage(
- boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) {
- // LazyFieldLite's are never null so we can just copy across. Necessary to avoid leakage
- // from builder into immutable message.
- // TODO(dweis): Change to null sentinels?
- mine.merge(other);
+ public LazyFieldLite visitLazyMessage(LazyFieldLite mine, LazyFieldLite other) {
+ if (other != null) {
+ if (mine == null) {
+ mine = new LazyFieldLite();
+ }
+ mine.merge(other);
+ }
return mine;
}
@@ -2140,7 +2173,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
-
+
return size > 0 ? mine : other;
}
@@ -2154,7 +2187,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
-
+
return size > 0 ? mine : other;
}
@@ -2168,7 +2201,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
-
+
return size > 0 ? mine : other;
}
@@ -2182,7 +2215,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
-
+
return size > 0 ? mine : other;
}
@@ -2196,7 +2229,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
-
+
return size > 0 ? mine : other;
}
@@ -2210,7 +2243,7 @@ public abstract class GeneratedMessageLite<
}
mine.addAll(other);
}
-
+
return size > 0 ? mine : other;
}
@@ -2232,10 +2265,15 @@ public abstract class GeneratedMessageLite<
return other == UnknownFieldSetLite.getDefaultInstance()
? mine : UnknownFieldSetLite.mutableCopyOf(mine, other);
}
-
+
@Override
public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
- mine.mergeFrom(other);
+ if (!other.isEmpty()) {
+ if (!mine.isMutable()) {
+ mine = mine.mutableCopy();
+ }
+ mine.mergeFrom(other);
+ }
return mine;
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/IntArrayList.java b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
index 6d6ece5a..2f526e3f 100644
--- a/java/core/src/main/java/com/google/protobuf/IntArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
@@ -38,25 +38,27 @@ 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 {
-
+final class IntArrayList
+ extends AbstractProtobufList<Integer>
+ implements IntList, RandomAccess {
+
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.
@@ -71,13 +73,14 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
}
/**
- * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}.
+ * Constructs a new mutable {@code IntArrayList}
+ * containing the same elements as {@code other}.
*/
- private IntArrayList(int[] array, int size) {
- this.array = array;
+ private IntArrayList(int[] other, int size) {
+ array = other;
this.size = size;
}
-
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -90,14 +93,14 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
if (size != other.size) {
return false;
}
-
+
final int[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
-
+
return true;
}
@@ -117,7 +120,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
}
return new IntArrayList(Arrays.copyOf(array, capacity), size);
}
-
+
@Override
public Integer get(int index) {
return getInt(index);
@@ -169,7 +172,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
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);
@@ -177,10 +180,10 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
// 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;
@@ -194,38 +197,38 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
@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();
@@ -254,7 +257,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
/**
* 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) {
diff --git a/java/core/src/main/java/com/google/protobuf/LongArrayList.java b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
index bc4475d1..5a772e3a 100644
--- a/java/core/src/main/java/com/google/protobuf/LongArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
@@ -38,25 +38,27 @@ 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 {
-
+final class LongArrayList
+ extends AbstractProtobufList<Long>
+ implements LongList, RandomAccess {
+
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.
@@ -71,33 +73,34 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
}
/**
- * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}.
+ * Constructs a new mutable {@code LongArrayList}
+ * containing the same elements as {@code other}.
*/
- private LongArrayList(long[] array, int size) {
- this.array = array;
+ private LongArrayList(long[] other, int size) {
+ array = other;
this.size = size;
}
-
+
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
- if (!(o instanceof IntArrayList)) {
+ if (!(o instanceof LongArrayList)) {
return super.equals(o);
}
LongArrayList other = (LongArrayList) o;
if (size != other.size) {
return false;
}
-
+
final long[] arr = other.array;
for (int i = 0; i < size; i++) {
if (array[i] != arr[i]) {
return false;
}
}
-
+
return true;
}
@@ -117,7 +120,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
}
return new LongArrayList(Arrays.copyOf(array, capacity), size);
}
-
+
@Override
public Long get(int index) {
return getLong(index);
@@ -169,7 +172,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
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);
@@ -177,10 +180,10 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
// 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;
@@ -194,38 +197,38 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
@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();
@@ -254,7 +257,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
/**
* 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) {
diff --git a/java/core/src/main/java/com/google/protobuf/MapEntry.java b/java/core/src/main/java/com/google/protobuf/MapEntry.java
index 31414bb4..117cd911 100644
--- a/java/core/src/main/java/com/google/protobuf/MapEntry.java
+++ b/java/core/src/main/java/com/google/protobuf/MapEntry.java
@@ -41,63 +41,83 @@ import java.util.TreeMap;
/**
* Implements MapEntry messages.
- *
+ *
* In reflection API, map fields will be treated as repeated message fields and
* each map entry is accessed as a message. This MapEntry class is used to
* represent these map entry messages in reflection API.
- *
+ *
* Protobuf internal. Users shouldn't use this class.
*/
public final class MapEntry<K, V> extends AbstractMessage {
- private static class Metadata<K, V> {
- public final Descriptor descriptor;
- public final MapEntry<K, V> defaultInstance;
- public final AbstractParser<MapEntry<K, V>> parser;
-
+
+ private static final class Metadata<K, V> extends MapEntryLite.Metadata<K, V> {
+
+ public final Descriptor descriptor;
+ public final Parser<MapEntry<K, V>> parser;
+
public Metadata(
- final Descriptor descriptor, final MapEntry<K, V> defaultInstance) {
+ Descriptor descriptor,
+ MapEntry<K, V> defaultInstance,
+ WireFormat.FieldType keyType,
+ WireFormat.FieldType valueType) {
+ super(keyType, defaultInstance.key, valueType, defaultInstance.value);
this.descriptor = descriptor;
- this.defaultInstance = defaultInstance;
- final Metadata<K, V> thisMetadata = this;
this.parser = new AbstractParser<MapEntry<K, V>>() {
- private final Parser<MapEntryLite<K, V>> dataParser =
- defaultInstance.data.getParserForType();
+
@Override
public MapEntry<K, V> parsePartialFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
- MapEntryLite<K, V> data =
- dataParser.parsePartialFrom(input, extensionRegistry);
- return new MapEntry<K, V>(thisMetadata, data);
+ return new MapEntry<K, V>(Metadata.this, input, extensionRegistry);
}
-
};
}
}
-
+
+ private final K key;
+ private final V value;
private final Metadata<K, V> metadata;
- private final MapEntryLite<K, V> data;
-
+
/** Create a default MapEntry instance. */
- private MapEntry(Descriptor descriptor,
+ private MapEntry(
+ Descriptor descriptor,
WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) {
- this.data = MapEntryLite.newDefaultInstance(
- keyType, defaultKey, valueType, defaultValue);
- this.metadata = new Metadata<K, V>(descriptor, this);
+ this.key = defaultKey;
+ this.value = defaultValue;
+ this.metadata = new Metadata<K, V>(descriptor, this, keyType, valueType);
}
-
- /** Create a new MapEntry message. */
- private MapEntry(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
+
+ /** Create a MapEntry with the provided key and value. */
+ private MapEntry(Metadata metadata, K key, V value) {
+ this.key = key;
+ this.value = value;
this.metadata = metadata;
- this.data = data;
}
-
+
+ /** Parsing constructor. */
+ private MapEntry(
+ Metadata<K, V> metadata,
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ try {
+ this.metadata = metadata;
+ Map.Entry<K, V> entry = MapEntryLite.parseEntry(input, metadata, extensionRegistry);
+ this.key = entry.getKey();
+ this.value = entry.getValue();
+ } catch (InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (IOException e) {
+ throw new InvalidProtocolBufferException(e.getMessage()).setUnfinishedMessage(this);
+ }
+ }
+
/**
* Create a default MapEntry instance. A default MapEntry instance should be
* created only once for each map entry message type. Generated code should
* store the created default instance and use it later to create new MapEntry
- * messages of the same type.
+ * messages of the same type.
*/
public static <K, V> MapEntry<K, V> newDefaultInstance(
Descriptor descriptor,
@@ -106,30 +126,38 @@ public final class MapEntry<K, V> extends AbstractMessage {
return new MapEntry<K, V>(
descriptor, keyType, defaultKey, valueType, defaultValue);
}
-
+
public K getKey() {
- return data.getKey();
+ return key;
}
-
+
public V getValue() {
- return data.getValue();
+ return value;
}
-
+
+ private volatile int cachedSerializedSize = -1;
+
@Override
public int getSerializedSize() {
- return data.getSerializedSize();
+ if (cachedSerializedSize != -1) {
+ return cachedSerializedSize;
+ }
+
+ int size = MapEntryLite.computeSerializedSize(metadata, key, value);
+ cachedSerializedSize = size;
+ return size;
}
-
+
@Override
public void writeTo(CodedOutputStream output) throws IOException {
- data.writeTo(output);
+ MapEntryLite.writeTo(output, metadata, key, value);
}
-
+
@Override
public boolean isInitialized() {
- return data.isInitialized();
+ return isInitialized(metadata, value);
}
-
+
@Override
public Parser<MapEntry<K, V>> getParserForType() {
return metadata.parser;
@@ -139,15 +167,15 @@ public final class MapEntry<K, V> extends AbstractMessage {
public Builder<K, V> newBuilderForType() {
return new Builder<K, V>(metadata);
}
-
+
@Override
public Builder<K, V> toBuilder() {
- return new Builder<K, V>(metadata, data);
+ return new Builder<K, V>(metadata, key, value);
}
@Override
public MapEntry<K, V> getDefaultInstanceForType() {
- return metadata.defaultInstance;
+ return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
}
@Override
@@ -157,8 +185,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public Map<FieldDescriptor, Object> getAllFields() {
- final TreeMap<FieldDescriptor, Object> result =
- new TreeMap<FieldDescriptor, Object>();
+ TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(field)) {
result.put(field, getField(field));
@@ -166,12 +193,12 @@ public final class MapEntry<K, V> extends AbstractMessage {
}
return Collections.unmodifiableMap(result);
}
-
+
private void checkFieldDescriptor(FieldDescriptor field) {
if (field.getContainingType() != metadata.descriptor) {
throw new RuntimeException(
"Wrong FieldDescriptor \"" + field.getFullName()
- + "\" used in message \"" + metadata.descriptor.getFullName());
+ + "\" used in message \"" + metadata.descriptor.getFullName());
}
}
@@ -217,56 +244,44 @@ public final class MapEntry<K, V> extends AbstractMessage {
public static class Builder<K, V>
extends AbstractMessage.Builder<Builder<K, V>> {
private final Metadata<K, V> metadata;
- private MapEntryLite<K, V> data;
- private MapEntryLite.Builder<K, V> dataBuilder;
-
+ private K key;
+ private V value;
+
private Builder(Metadata<K, V> metadata) {
- this.metadata = metadata;
- this.data = metadata.defaultInstance.data;
- this.dataBuilder = null;
+ this(metadata, metadata.defaultKey, metadata.defaultValue);
}
-
- private Builder(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
+
+ private Builder(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata;
- this.data = data;
- this.dataBuilder = null;
+ this.key = key;
+ this.value = value;
}
-
+
public K getKey() {
- return dataBuilder == null ? data.getKey() : dataBuilder.getKey();
+ return key;
}
-
+
public V getValue() {
- return dataBuilder == null ? data.getValue() : dataBuilder.getValue();
- }
-
- private void ensureMutable() {
- if (dataBuilder == null) {
- dataBuilder = data.toBuilder();
- }
+ return value;
}
-
+
public Builder<K, V> setKey(K key) {
- ensureMutable();
- dataBuilder.setKey(key);
+ this.key = key;
return this;
}
-
+
public Builder<K, V> clearKey() {
- ensureMutable();
- dataBuilder.clearKey();
+ this.key = metadata.defaultKey;
return this;
}
-
+
public Builder<K, V> setValue(V value) {
- ensureMutable();
- dataBuilder.setValue(value);
+ this.value = value;
return this;
}
-
+
public Builder<K, V> clearValue() {
- ensureMutable();
- dataBuilder.clearValue();
+ this.value = metadata.defaultValue;
return this;
}
@@ -281,29 +296,24 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public MapEntry<K, V> buildPartial() {
- if (dataBuilder != null) {
- data = dataBuilder.buildPartial();
- dataBuilder = null;
- }
- return new MapEntry<K, V>(metadata, data);
+ return new MapEntry<K, V>(metadata, key, value);
}
@Override
public Descriptor getDescriptorForType() {
return metadata.descriptor;
}
-
+
private void checkFieldDescriptor(FieldDescriptor field) {
if (field.getContainingType() != metadata.descriptor) {
throw new RuntimeException(
"Wrong FieldDescriptor \"" + field.getFullName()
- + "\" used in message \"" + metadata.descriptor.getFullName());
+ + "\" used in message \"" + metadata.descriptor.getFullName());
}
}
@Override
- public com.google.protobuf.Message.Builder newBuilderForField(
- FieldDescriptor field) {
+ public Message.Builder newBuilderForField(FieldDescriptor field) {
checkFieldDescriptor(field);;
// This method should be called for message fields and in a MapEntry
// message only the value field can possibly be a message field.
@@ -312,7 +322,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
throw new RuntimeException(
"\"" + field.getFullName() + "\" is not a message value field.");
}
- return ((Message) data.getValue()).newBuilderForType();
+ return ((Message) value).newBuilderForType();
}
@SuppressWarnings("unchecked")
@@ -362,22 +372,17 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public MapEntry<K, V> getDefaultInstanceForType() {
- return metadata.defaultInstance;
+ return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
}
@Override
public boolean isInitialized() {
- if (dataBuilder != null) {
- return dataBuilder.isInitialized();
- } else {
- return data.isInitialized();
- }
+ return MapEntry.isInitialized(metadata, value);
}
@Override
public Map<FieldDescriptor, Object> getAllFields() {
- final TreeMap<FieldDescriptor, Object> result =
- new TreeMap<FieldDescriptor, Object>();
+ final TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(field)) {
result.put(field, getField(field));
@@ -398,8 +403,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
Object result = field.getNumber() == 1 ? getKey() : getValue();
// Convert enums to EnumValueDescriptor.
if (field.getType() == FieldDescriptor.Type.ENUM) {
- result = field.getEnumType().findValueByNumberCreatingIfUnknown(
- (java.lang.Integer) result);
+ result = field.getEnumType().findValueByNumberCreatingIfUnknown((Integer) result);
}
return result;
}
@@ -409,13 +413,13 @@ public final class MapEntry<K, V> extends AbstractMessage {
throw new RuntimeException(
"There is no repeated field in a map entry message.");
}
-
+
@Override
public Object getRepeatedField(FieldDescriptor field, int index) {
throw new RuntimeException(
"There is no repeated field in a map entry message.");
}
-
+
@Override
public UnknownFieldSet getUnknownFields() {
return UnknownFieldSet.getDefaultInstance();
@@ -423,11 +427,14 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public Builder<K, V> clone() {
- if (dataBuilder == null) {
- return new Builder<K, V>(metadata, data);
- } else {
- return new Builder<K, V>(metadata, dataBuilder.build());
- }
+ return new Builder(metadata, key, value);
}
}
+
+ private static <V> boolean isInitialized(Metadata metadata, V value) {
+ if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
+ return ((MessageLite) value).isInitialized();
+ }
+ return true;
+ }
}
diff --git a/java/core/src/main/java/com/google/protobuf/MapEntryLite.java b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
index 12c64abb..22aef8f9 100644
--- a/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
@@ -31,80 +31,74 @@
package com.google.protobuf;
import java.io.IOException;
+import java.util.AbstractMap;
+import java.util.Map;
/**
* Implements the lite version of map entry messages.
- *
+ *
* This class serves as an utility class to help do serialization/parsing of
* map entries. It's used in generated code and also in the full version
* MapEntry message.
- *
+ *
* Protobuf internal. Users shouldn't use.
*/
-public class MapEntryLite<K, V>
- extends AbstractMessageLite<MapEntryLite<K, V>, MapEntryLite.Builder<K, V>> {
- private static class Metadata<K, V> {
- public final MapEntryLite<K, V> defaultInstance;
+public class MapEntryLite<K, V> {
+
+ static class Metadata<K, V> {
public final WireFormat.FieldType keyType;
+ public final K defaultKey;
public final WireFormat.FieldType valueType;
- public final Parser<MapEntryLite<K, V>> parser;
+ public final V defaultValue;
+
public Metadata(
- MapEntryLite<K, V> defaultInstance,
- WireFormat.FieldType keyType,
- WireFormat.FieldType valueType) {
- this.defaultInstance = defaultInstance;
+ WireFormat.FieldType keyType, K defaultKey,
+ WireFormat.FieldType valueType, V defaultValue) {
this.keyType = keyType;
+ this.defaultKey = defaultKey;
this.valueType = valueType;
- final Metadata<K, V> finalThis = this;
- this.parser = new AbstractParser<MapEntryLite<K, V>>() {
- @Override
- public MapEntryLite<K, V> parsePartialFrom(
- CodedInputStream input, ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException {
- return new MapEntryLite<K, V>(finalThis, input, extensionRegistry);
- }
- };
+ this.defaultValue = defaultValue;
}
}
-
+
private static final int KEY_FIELD_NUMBER = 1;
private static final int VALUE_FIELD_NUMBER = 2;
-
+
private final Metadata<K, V> metadata;
private final K key;
private final V value;
-
+
/** Creates a default MapEntryLite message instance. */
private MapEntryLite(
WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) {
- this.metadata = new Metadata<K, V>(this, keyType, valueType);
+ this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue);
this.key = defaultKey;
this.value = defaultValue;
}
-
+
/** Creates a new MapEntryLite message. */
private MapEntryLite(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata;
this.key = key;
this.value = value;
}
-
+
public K getKey() {
return key;
}
-
+
public V getValue() {
return value;
}
/**
* Creates a default MapEntryLite message instance.
- *
+ *
* This method is used by generated code to create the default instance for
* a map entry message. The created default instance should be used to create
* new map entry messages of the same type. For each map entry message, only
- * one default instance should be created.
+ * one default instance should be created.
*/
public static <K, V> MapEntryLite<K, V> newDefaultInstance(
WireFormat.FieldType keyType, K defaultKey,
@@ -112,80 +106,20 @@ public class MapEntryLite<K, V>
return new MapEntryLite<K, V>(
keyType, defaultKey, valueType, defaultValue);
}
-
- @Override
- public void writeTo(CodedOutputStream output) throws IOException {
- writeField(KEY_FIELD_NUMBER, metadata.keyType, key, output);
- writeField(VALUE_FIELD_NUMBER, metadata.valueType, value, output);
- }
- private void writeField(
- int number, WireFormat.FieldType type, Object value,
- CodedOutputStream output) throws IOException {
- output.writeTag(number, type.getWireType());
- FieldSet.writeElementNoTag(output, type, value);
+ static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)
+ throws IOException {
+ FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key);
+ FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value);
}
- private volatile int cachedSerializedSize = -1;
- @Override
- public int getSerializedSize() {
- if (cachedSerializedSize != -1) {
- return cachedSerializedSize;
- }
- int size = 0;
- size += getFieldSize(KEY_FIELD_NUMBER, metadata.keyType, key);
- size += getFieldSize(VALUE_FIELD_NUMBER, metadata.valueType, value);
- cachedSerializedSize = size;
- return size;
+ static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) {
+ return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key)
+ + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value);
}
- private int getFieldSize(
- int number, WireFormat.FieldType type, Object value) {
- return CodedOutputStream.computeTagSize(number)
- + FieldSet.computeElementSizeNoTag(type, value);
- }
-
- /** Parsing constructor. */
- private MapEntryLite(
- Metadata<K, V> metadata,
- CodedInputStream input,
- ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException {
- try {
- K key = metadata.defaultInstance.key;
- V value = metadata.defaultInstance.value;
- while (true) {
- int tag = input.readTag();
- if (tag == 0) {
- break;
- }
- if (tag == WireFormat.makeTag(
- KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
- key = mergeField(
- input, extensionRegistry, metadata.keyType, key);
- } else if (tag == WireFormat.makeTag(
- VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
- value = mergeField(
- input, extensionRegistry, metadata.valueType, value);
- } else {
- if (!input.skipField(tag)) {
- break;
- }
- }
- }
- this.metadata = metadata;
- this.key = key;
- this.value = value;
- } catch (InvalidProtocolBufferException e) {
- throw e.setUnfinishedMessage(this);
- } catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage())
- .setUnfinishedMessage(this);
- }
- }
-
@SuppressWarnings("unchecked")
- private <T> T mergeField(
+ static <T> T parseField(
CodedInputStream input, ExtensionRegistryLite extensionRegistry,
WireFormat.FieldType type, T value) throws IOException {
switch (type) {
@@ -202,136 +136,91 @@ public class MapEntryLite<K, V>
}
}
- @Override
- public Parser<MapEntryLite<K, V>> getParserForType() {
- return metadata.parser;
- }
-
- @Override
- public Builder<K, V> newBuilderForType() {
- return new Builder<K, V>(metadata);
- }
-
- @Override
- public Builder<K, V> toBuilder() {
- return new Builder<K, V>(metadata, key, value);
+ /**
+ * Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite}
+ * to the output stream. This helper method avoids allocation of a {@link MapEntryLite}
+ * built with a key and value and is called from generated code directly.
+ */
+ public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)
+ throws IOException {
+ output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeUInt32NoTag(computeSerializedSize(metadata, key, value));
+ writeTo(output, metadata, key, value);
}
- @Override
- public MapEntryLite<K, V> getDefaultInstanceForType() {
- return metadata.defaultInstance;
+ /**
+ * Computes the message size for the provided key and value as though they were wrapped
+ * by a {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite}
+ * built with a key and value and is called from generated code directly.
+ */
+ public int computeMessageSize(int fieldNumber, K key, V value) {
+ return CodedOutputStream.computeTagSize(fieldNumber)
+ + CodedOutputStream.computeLengthDelimitedFieldSize(
+ computeSerializedSize(metadata, key, value));
}
- @Override
- public boolean isInitialized() {
- if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
- return ((MessageLite) value).isInitialized();
+ /**
+ * Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation
+ * so using {@link #parseInto} is preferred if possible.
+ */
+ public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry);
+ }
+
+ static <K, V> Map.Entry<K, V> parseEntry(
+ CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)
+ throws IOException{
+ K key = metadata.defaultKey;
+ V value = metadata.defaultValue;
+ while (true) {
+ int tag = input.readTag();
+ if (tag == 0) {
+ break;
+ }
+ if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
+ key = parseField(input, extensionRegistry, metadata.keyType, key);
+ } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
+ value = parseField(input, extensionRegistry, metadata.valueType, value);
+ } else {
+ if (!input.skipField(tag)) {
+ break;
+ }
+ }
}
- return true;
+ return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
}
/**
- * Builder used to create {@link MapEntryLite} messages.
+ * Parses an entry off of the input into the map. This helper avoids allocaton of a
+ * {@link MapEntryLite} by parsing directly into the provided {@link MapFieldLite}.
*/
- public static class Builder<K, V>
- extends AbstractMessageLite.Builder<MapEntryLite<K, V>, Builder<K, V>> {
- private final Metadata<K, V> metadata;
- private K key;
- private V value;
-
- private Builder(Metadata<K, V> metadata) {
- this.metadata = metadata;
- this.key = metadata.defaultInstance.key;
- this.value = metadata.defaultInstance.value;
- }
-
- public K getKey() {
- return key;
- }
-
- public V getValue() {
- return value;
- }
-
- public Builder<K, V> setKey(K key) {
- this.key = key;
- return this;
- }
-
- public Builder<K, V> setValue(V value) {
- this.value = value;
- return this;
- }
-
- public Builder<K, V> clearKey() {
- this.key = metadata.defaultInstance.key;
- return this;
- }
-
- public Builder<K, V> clearValue() {
- this.value = metadata.defaultInstance.value;
- return this;
- }
-
- @Override
- public Builder<K, V> clear() {
- this.key = metadata.defaultInstance.key;
- this.value = metadata.defaultInstance.value;
- return this;
- }
-
- @Override
- public MapEntryLite<K, V> build() {
- MapEntryLite<K, V> result = buildPartial();
- if (!result.isInitialized()) {
- throw newUninitializedMessageException(result);
+ public void parseInto(
+ MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ int length = input.readRawVarint32();
+ final int oldLimit = input.pushLimit(length);
+ K key = metadata.defaultKey;
+ V value = metadata.defaultValue;
+
+ while (true) {
+ int tag = input.readTag();
+ if (tag == 0) {
+ break;
}
- return result;
- }
-
- @Override
- public MapEntryLite<K, V> buildPartial() {
- return new MapEntryLite<K, V>(metadata, key, value);
- }
-
- @Override
- public MessageLite getDefaultInstanceForType() {
- return metadata.defaultInstance;
- }
-
- @Override
- public boolean isInitialized() {
- if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
- return ((MessageLite) value).isInitialized();
+ if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
+ key = parseField(input, extensionRegistry, metadata.keyType, key);
+ } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
+ value = parseField(input, extensionRegistry, metadata.valueType, value);
+ } else {
+ if (!input.skipField(tag)) {
+ break;
+ }
}
- return true;
- }
-
- private Builder(Metadata<K, V> metadata, K key, V value) {
- this.metadata = metadata;
- this.key = key;
- this.value = value;
- }
-
- @Override
- public Builder<K, V> clone() {
- return new Builder<K, V>(metadata, key, value);
}
- @Override
- public Builder<K, V> mergeFrom(
- CodedInputStream input, ExtensionRegistryLite extensionRegistry)
- throws IOException {
- MapEntryLite<K, V> entry =
- new MapEntryLite<K, V>(metadata, input, extensionRegistry);
- this.key = entry.key;
- this.value = entry.value;
- return this;
- }
-
- @Override
- protected Builder<K, V> internalMergeFrom(MapEntryLite<K, V> message) {
- throw new UnsupportedOperationException();
- }
+ input.checkLastTagWas(0);
+ input.popLimit(oldLimit);
+ map.put(key, value);
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/MapField.java b/java/core/src/main/java/com/google/protobuf/MapField.java
index 907f0f71..a6109f98 100644
--- a/java/core/src/main/java/com/google/protobuf/MapField.java
+++ b/java/core/src/main/java/com/google/protobuf/MapField.java
@@ -30,25 +30,26 @@
package com.google.protobuf;
-import com.google.protobuf.MapFieldLite.MutatabilityAwareMap;
-
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Internal representation of map fields in generated messages.
- *
+ *
* This class supports accessing the map field as a {@link Map} to be used in
* generated API and also supports accessing the field as a {@link List} to be
* used in reflection API. It keeps track of where the data is currently stored
- * and do necessary conversions between map and list.
- *
+ * and do necessary conversions between map and list.
+ *
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
- *
+ *
* THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap()
* and getList() concurrently in multiple threads. If write-access is needed,
* all access must be synchronized.
@@ -56,21 +57,21 @@ import java.util.Map;
public class MapField<K, V> implements MutabilityOracle {
/**
* Indicates where the data of this map field is currently stored.
- *
+ *
* MAP: Data is stored in mapData.
* LIST: Data is stored in listData.
* BOTH: mapData and listData have the same data.
*
* When the map field is accessed (through generated API or reflection API),
* it will shift between these 3 modes:
- *
+ *
* getMap() getList() getMutableMap() getMutableList()
* MAP MAP BOTH MAP LIST
* LIST BOTH LIST MAP LIST
* BOTH BOTH BOTH MAP LIST
- *
+ *
* As the map field changes its mode, the list/map reference returned in a
- * previous method call may be invalidated.
+ * previous method call may be invalidated.
*/
private enum StorageMode {MAP, LIST, BOTH}
@@ -78,26 +79,26 @@ public class MapField<K, V> implements MutabilityOracle {
private volatile StorageMode mode;
private MutatabilityAwareMap<K, V> mapData;
private List<Message> listData;
-
+
// Convert between a map entry Message and a key-value pair.
private static interface Converter<K, V> {
Message convertKeyAndValueToMessage(K key, V value);
void convertMessageToKeyAndValue(Message message, Map<K, V> map);
-
+
Message getMessageDefaultInstance();
}
-
+
private static class ImmutableMessageConverter<K, V> implements Converter<K, V> {
private final MapEntry<K, V> defaultEntry;
public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) {
this.defaultEntry = defaultEntry;
}
-
+
@Override
public Message convertKeyAndValueToMessage(K key, V value) {
return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial();
}
-
+
@Override
public void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
MapEntry<K, V> entry = (MapEntry<K, V>) message;
@@ -109,10 +110,10 @@ public class MapField<K, V> implements MutabilityOracle {
return defaultEntry;
}
}
-
+
private final Converter<K, V> converter;
-
+
private MapField(
Converter<K, V> converter,
StorageMode mode,
@@ -123,34 +124,34 @@ public class MapField<K, V> implements MutabilityOracle {
this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
this.listData = null;
}
-
+
private MapField(
MapEntry<K, V> defaultEntry,
StorageMode mode,
Map<K, V> mapData) {
this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData);
}
-
-
+
+
/** Returns an immutable empty MapField. */
public static <K, V> MapField<K, V> emptyMapField(
MapEntry<K, V> defaultEntry) {
return new MapField<K, V>(
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 LinkedHashMap<K, V>());
}
-
-
+
+
private Message convertKeyAndValueToMessage(K key, V value) {
return converter.convertKeyAndValueToMessage(key, value);
}
-
+
@SuppressWarnings("unchecked")
private void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
converter.convertMessageToKeyAndValue(message, map);
@@ -173,7 +174,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return new MutatabilityAwareMap<K, V>(this, mapData);
}
-
+
/** Returns the content of this MapField as a read-only Map. */
public Map<K, V> getMap() {
if (mode == StorageMode.LIST) {
@@ -186,7 +187,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return Collections.unmodifiableMap(mapData);
}
-
+
/** Gets a mutable Map view of this MapField. */
public Map<K, V> getMutableMap() {
if (mode != StorageMode.MAP) {
@@ -194,20 +195,20 @@ public class MapField<K, V> implements MutabilityOracle {
mapData = convertListToMap(listData);
}
listData = null;
- mode = StorageMode.MAP;
+ mode = StorageMode.MAP;
}
return mapData;
}
-
+
public void mergeFrom(MapField<K, V> other) {
getMutableMap().putAll(MapFieldLite.copy(other.getMap()));
}
-
+
public void clear() {
mapData = new MutatabilityAwareMap<K, V>(this, new LinkedHashMap<K, V>());
mode = StorageMode.MAP;
}
-
+
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
@@ -217,18 +218,18 @@ public class MapField<K, V> implements MutabilityOracle {
MapField<K, V> other = (MapField<K, V>) object;
return MapFieldLite.<K, V>equals(getMap(), other.getMap());
}
-
+
@Override
public int hashCode() {
return MapFieldLite.<K, V>calculateHashCodeForMap(getMap());
}
-
+
/** Returns a deep copy of this MapField. */
public MapField<K, V> copy() {
return new MapField<K, V>(
converter, StorageMode.MAP, MapFieldLite.copy(getMap()));
}
-
+
/** Gets the content of this MapField as a read-only List. */
List<Message> getList() {
if (mode == StorageMode.MAP) {
@@ -241,7 +242,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return Collections.unmodifiableList(listData);
}
-
+
/** Gets a mutable List view of this MapField. */
List<Message> getMutableList() {
if (mode != StorageMode.LIST) {
@@ -253,7 +254,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return listData;
}
-
+
/**
* Gets the default instance of the message stored in the list view of this
* map field.
@@ -261,7 +262,7 @@ public class MapField<K, V> implements MutabilityOracle {
Message getMapEntryMessageDefaultInstance() {
return converter.getMessageDefaultInstance();
}
-
+
/**
* Makes this list immutable. All subsequent modifications will throw an
* {@link UnsupportedOperationException}.
@@ -269,14 +270,14 @@ public class MapField<K, V> implements MutabilityOracle {
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()
*/
@@ -286,4 +287,338 @@ public class MapField<K, V> implements MutabilityOracle {
throw new UnsupportedOperationException();
}
}
+
+ /**
+ * An internal map that checks for mutability before delegating.
+ */
+ private 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/core/src/main/java/com/google/protobuf/MapFieldLite.java b/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
index 960b6339..3c0ad89a 100644
--- a/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
@@ -33,71 +33,85 @@ package com.google.protobuf;
import com.google.protobuf.Internal.EnumLite;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-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.
- *
+ *
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
*/
-public final class MapFieldLite<K, V> implements MutabilityOracle {
- private MutatabilityAwareMap<K, V> mapData;
+public final class MapFieldLite<K, V> extends LinkedHashMap<K, V> {
+
private boolean isMutable;
-
+
+ private MapFieldLite() {
+ this.isMutable = true;
+ }
+
private MapFieldLite(Map<K, V> mapData) {
- this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
+ super(mapData);
this.isMutable = true;
}
-
+
@SuppressWarnings({"rawtypes", "unchecked"})
- private static final MapFieldLite EMPTY_MAP_FIELD =
- new MapFieldLite(Collections.emptyMap());
+ 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"})
public static <K, V> MapFieldLite<K, V> emptyMapField() {
return (MapFieldLite<K, V>) EMPTY_MAP_FIELD;
}
-
- /** Creates a new MapFieldLite instance. */
- public static <K, V> MapFieldLite<K, V> newMapField() {
- return new MapFieldLite<K, V>(new LinkedHashMap<K, V>());
+
+ public void mergeFrom(MapFieldLite<K, V> other) {
+ ensureMutable();
+ if (!other.isEmpty()) {
+ putAll(other);
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "cast"})
+ @Override public Set<Map.Entry<K, V>> entrySet() {
+ return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet();
+ }
+
+ @Override public void clear() {
+ ensureMutable();
+ clear();
}
-
- /** Gets the content of this MapField as a read-only Map. */
- public Map<K, V> getMap() {
- return Collections.unmodifiableMap(mapData);
+
+ @Override public V put(K key, V value) {
+ ensureMutable();
+ return super.put(key, value);
}
-
- /** Gets a mutable Map view of this MapField. */
- public Map<K, V> getMutableMap() {
- return mapData;
+
+ public V put(Map.Entry<K, V> entry) {
+ return put(entry.getKey(), entry.getValue());
}
-
- public void mergeFrom(MapFieldLite<K, V> other) {
- mapData.putAll(copy(other.mapData));
+
+ @Override public void putAll(Map<? extends K, ? extends V> m) {
+ ensureMutable();
+ super.putAll(m);
}
-
- public void clear() {
- mapData.clear();
+
+ @Override public V remove(Object key) {
+ ensureMutable();
+ return super.remove(key);
}
-
+
private static boolean equals(Object a, Object b) {
if (a instanceof byte[] && b instanceof byte[]) {
return Arrays.equals((byte[]) a, (byte[]) b);
}
return a.equals(b);
}
-
+
/**
* Checks whether two {@link Map}s are equal. We don't use the default equals
* method of {@link Map} because it compares by identity not by content for
@@ -120,20 +134,16 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return true;
}
-
+
/**
* Checks whether two map fields are equal.
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
- if (!(object instanceof MapFieldLite)) {
- return false;
- }
- MapFieldLite<K, V> other = (MapFieldLite<K, V>) object;
- return equals(mapData, other.mapData);
+ return (object instanceof Map) && equals(this, (Map<K, V>) object);
}
-
+
private static int calculateHashCodeForObject(Object a) {
if (a instanceof byte[]) {
return Internal.hashCode((byte[]) a);
@@ -156,14 +166,14 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
result += calculateHashCodeForObject(entry.getKey())
^ calculateHashCodeForObject(entry.getValue());
}
- return result;
+ return result;
}
-
+
@Override
public int hashCode() {
- return calculateHashCodeForMap(mapData);
+ return calculateHashCodeForMap(this);
}
-
+
private static Object copy(Object object) {
if (object instanceof byte[]) {
byte[] data = (byte[]) object;
@@ -171,7 +181,7 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return object;
}
-
+
/**
* Makes a deep copy of a {@link Map}. Immutable objects in the map will be
* shared (e.g., integers, strings, immutable messages) and mutable ones will
@@ -185,12 +195,12 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return result;
}
-
+
/** Returns a deep copy of this map field. */
- public MapFieldLite<K, V> copy() {
- return new MapFieldLite<K, V>(copy(mapData));
+ public MapFieldLite<K, V> mutableCopy() {
+ return isEmpty() ? new MapFieldLite<K, V>() : new MapFieldLite<K, V>(this);
}
-
+
/**
* Makes this field immutable. All subsequent modifications will throw an
* {@link UnsupportedOperationException}.
@@ -198,352 +208,17 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
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.
+ private void ensureMutable() {
+ if (!isMutable()) {
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/core/src/main/java/com/google/protobuf/MessageReflection.java b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
index 7b791d9e..3d73efb3 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageReflection.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
@@ -364,7 +364,6 @@ class MessageReflection {
* Finishes the merge and returns the underlying object.
*/
Object finish();
-
}
static class BuilderAdapter implements MergeTarget {
@@ -549,7 +548,6 @@ class MessageReflection {
public Object finish() {
return builder.buildPartial();
}
-
}
@@ -713,7 +711,6 @@ class MessageReflection {
throw new UnsupportedOperationException(
"finish() called on FieldSet object");
}
-
}
/**
diff --git a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java
new file mode 100644
index 00000000..77b61b5f
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java
@@ -0,0 +1,708 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@code RepeatedFieldBuilderV3} implements a structure that a protocol
+ * message uses to hold a repeated field of other protocol messages. It supports
+ * the classical use case of adding immutable {@link Message}'s to the
+ * repeated field and is highly optimized around this (no extra memory
+ * allocations and sharing of immutable arrays).
+ * <br>
+ * It also supports the additional use case of adding a {@link Message.Builder}
+ * to the repeated field and deferring conversion of that {@code Builder}
+ * to an immutable {@code Message}. In this way, it's possible to maintain
+ * a tree of {@code Builder}'s that acts as a fully read/write data
+ * structure.
+ * <br>
+ * Logically, one can think of a tree of builders as converting the entire tree
+ * to messages when build is called on the root or when any method is called
+ * that desires a Message instead of a Builder. In terms of the implementation,
+ * the {@code SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3}
+ * classes cache messages that were created so that messages only need to be
+ * created when some change occurred in its builder or a builder for one of its
+ * descendants.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ *
+ * @author jonp@google.com (Jon Perlow)
+ */
+public class RepeatedFieldBuilderV3
+ <MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ implements AbstractMessage.BuilderParent {
+
+ // Parent to send changes to.
+ private AbstractMessage.BuilderParent parent;
+
+ // List of messages. Never null. It may be immutable, in which case
+ // isMessagesListMutable will be false. See note below.
+ private List<MType> messages;
+
+ // Whether messages is an mutable array that can be modified.
+ private boolean isMessagesListMutable;
+
+ // List of builders. May be null, in which case, no nested builders were
+ // created. If not null, entries represent the builder for that index.
+ private List<SingleFieldBuilderV3<MType, BType, IType>> builders;
+
+ // Here are the invariants for messages and builders:
+ // 1. messages is never null and its count corresponds to the number of items
+ // in the repeated field.
+ // 2. If builders is non-null, messages and builders MUST always
+ // contain the same number of items.
+ // 3. Entries in either array can be null, but for any index, there MUST be
+ // either a Message in messages or a builder in builders.
+ // 4. If the builder at an index is non-null, the builder is
+ // authoritative. This is the case where a Builder was set on the index.
+ // Any message in the messages array MUST be ignored.
+ // t. If the builder at an index is null, the message in the messages
+ // list is authoritative. This is the case where a Message (not a Builder)
+ // was set directly for an index.
+
+ // Indicates that we've built a message and so we are now obligated
+ // to dispatch dirty invalidations. See AbstractMessage.BuilderListener.
+ private boolean isClean;
+
+ // A view of this builder that exposes a List interface of messages. This is
+ // initialized on demand. This is fully backed by this object and all changes
+ // are reflected in it. Access to any item converts it to a message if it
+ // was a builder.
+ private MessageExternalList<MType, BType, IType> externalMessageList;
+
+ // A view of this builder that exposes a List interface of builders. This is
+ // initialized on demand. This is fully backed by this object and all changes
+ // are reflected in it. Access to any item converts it to a builder if it
+ // was a message.
+ private BuilderExternalList<MType, BType, IType> externalBuilderList;
+
+ // A view of this builder that exposes a List interface of the interface
+ // implemented by messages and builders. This is initialized on demand. This
+ // is fully backed by this object and all changes are reflected in it.
+ // Access to any item returns either a builder or message depending on
+ // what is most efficient.
+ private MessageOrBuilderExternalList<MType, BType, IType>
+ externalMessageOrBuilderList;
+
+ /**
+ * Constructs a new builder with an empty list of messages.
+ *
+ * @param messages the current list of messages
+ * @param isMessagesListMutable Whether the messages list is mutable
+ * @param parent a listener to notify of changes
+ * @param isClean whether the builder is initially marked clean
+ */
+ public RepeatedFieldBuilderV3(
+ List<MType> messages,
+ boolean isMessagesListMutable,
+ AbstractMessage.BuilderParent parent,
+ boolean isClean) {
+ this.messages = messages;
+ this.isMessagesListMutable = isMessagesListMutable;
+ this.parent = parent;
+ this.isClean = isClean;
+ }
+
+ public void dispose() {
+ // Null out parent so we stop sending it invalidations.
+ parent = null;
+ }
+
+ /**
+ * Ensures that the list of messages is mutable so it can be updated. If it's
+ * immutable, a copy is made.
+ */
+ private void ensureMutableMessageList() {
+ if (!isMessagesListMutable) {
+ messages = new ArrayList<MType>(messages);
+ isMessagesListMutable = true;
+ }
+ }
+
+ /**
+ * Ensures that the list of builders is not null. If it's null, the list is
+ * created and initialized to be the same size as the messages list with
+ * null entries.
+ */
+ private void ensureBuilders() {
+ if (this.builders == null) {
+ this.builders =
+ new ArrayList<SingleFieldBuilderV3<MType, BType, IType>>(
+ messages.size());
+ for (int i = 0; i < messages.size(); i++) {
+ builders.add(null);
+ }
+ }
+ }
+
+ /**
+ * Gets the count of items in the list.
+ *
+ * @return the count of items in the list.
+ */
+ public int getCount() {
+ return messages.size();
+ }
+
+ /**
+ * Gets whether the list is empty.
+ *
+ * @return whether the list is empty
+ */
+ public boolean isEmpty() {
+ return messages.isEmpty();
+ }
+
+ /**
+ * Get the message at the specified index. If the message is currently stored
+ * as a {@code Builder}, it is converted to a {@code Message} by
+ * calling {@link Message.Builder#buildPartial} on it.
+ *
+ * @param index the index of the message to get
+ * @return the message for the specified index
+ */
+ public MType getMessage(int index) {
+ return getMessage(index, false);
+ }
+
+ /**
+ * Get the message at the specified index. If the message is currently stored
+ * as a {@code Builder}, it is converted to a {@code Message} by
+ * calling {@link Message.Builder#buildPartial} on it.
+ *
+ * @param index the index of the message to get
+ * @param forBuild this is being called for build so we want to make sure
+ * we SingleFieldBuilderV3.build to send dirty invalidations
+ * @return the message for the specified index
+ */
+ private MType getMessage(int index, boolean forBuild) {
+ if (this.builders == null) {
+ // We don't have any builders -- return the current Message.
+ // This is the case where no builder was created, so we MUST have a
+ // Message.
+ return messages.get(index);
+ }
+
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
+ if (builder == null) {
+ // We don't have a builder -- return the current message.
+ // This is the case where no builder was created for the entry at index,
+ // so we MUST have a message.
+ return messages.get(index);
+
+ } else {
+ return forBuild ? builder.build() : builder.getMessage();
+ }
+ }
+
+ /**
+ * Gets a builder for the specified index. If no builder has been created for
+ * that index, a builder is created on demand by calling
+ * {@link Message#toBuilder}.
+ *
+ * @param index the index of the message to get
+ * @return The builder for that index
+ */
+ public BType getBuilder(int index) {
+ ensureBuilders();
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
+ if (builder == null) {
+ MType message = messages.get(index);
+ builder = new SingleFieldBuilderV3<MType, BType, IType>(
+ message, this, isClean);
+ builders.set(index, builder);
+ }
+ return builder.getBuilder();
+ }
+
+ /**
+ * Gets the base class interface for the specified index. This may either be
+ * a builder or a message. It will return whatever is more efficient.
+ *
+ * @param index the index of the message to get
+ * @return the message or builder for the index as the base class interface
+ */
+ @SuppressWarnings("unchecked")
+ public IType getMessageOrBuilder(int index) {
+ if (this.builders == null) {
+ // We don't have any builders -- return the current Message.
+ // This is the case where no builder was created, so we MUST have a
+ // Message.
+ return (IType) messages.get(index);
+ }
+
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
+ if (builder == null) {
+ // We don't have a builder -- return the current message.
+ // This is the case where no builder was created for the entry at index,
+ // so we MUST have a message.
+ return (IType) messages.get(index);
+
+ } else {
+ return builder.getMessageOrBuilder();
+ }
+ }
+
+ /**
+ * Sets a message at the specified index replacing the existing item at
+ * that index.
+ *
+ * @param index the index to set.
+ * @param message the message to set
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> setMessage(
+ int index, MType message) {
+ if (message == null) {
+ throw new NullPointerException();
+ }
+ ensureMutableMessageList();
+ messages.set(index, message);
+ if (builders != null) {
+ SingleFieldBuilderV3<MType, BType, IType> entry =
+ builders.set(index, null);
+ if (entry != null) {
+ entry.dispose();
+ }
+ }
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Appends the specified element to the end of this list.
+ *
+ * @param message the message to add
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> addMessage(
+ MType message) {
+ if (message == null) {
+ throw new NullPointerException();
+ }
+ ensureMutableMessageList();
+ messages.add(message);
+ if (builders != null) {
+ builders.add(null);
+ }
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Inserts the specified message at the specified position in this list.
+ * Shifts the element currently at that position (if any) and any subsequent
+ * elements to the right (adds one to their indices).
+ *
+ * @param index the index at which to insert the message
+ * @param message the message to add
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> addMessage(
+ int index, MType message) {
+ if (message == null) {
+ throw new NullPointerException();
+ }
+ ensureMutableMessageList();
+ messages.add(index, message);
+ if (builders != null) {
+ builders.add(index, null);
+ }
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Appends all of the messages in the specified collection to the end of
+ * this list, in the order that they are returned by the specified
+ * collection's iterator.
+ *
+ * @param values the messages to add
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> addAllMessages(
+ Iterable<? extends MType> values) {
+ for (final MType value : values) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ }
+
+ // If we can inspect the size, we can more efficiently add messages.
+ int size = -1;
+ if (values instanceof Collection) {
+ @SuppressWarnings("unchecked") final
+ Collection<MType> collection = (Collection<MType>) values;
+ if (collection.size() == 0) {
+ return this;
+ }
+ size = collection.size();
+ }
+ ensureMutableMessageList();
+
+ if (size >= 0 && messages instanceof ArrayList) {
+ ((ArrayList<MType>) messages)
+ .ensureCapacity(messages.size() + size);
+ }
+
+ for (MType value : values) {
+ addMessage(value);
+ }
+
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Appends a new builder to the end of this list and returns the builder.
+ *
+ * @param message the message to add which is the basis of the builder
+ * @return the new builder
+ */
+ public BType addBuilder(MType message) {
+ ensureMutableMessageList();
+ ensureBuilders();
+ SingleFieldBuilderV3<MType, BType, IType> builder =
+ new SingleFieldBuilderV3<MType, BType, IType>(
+ message, this, isClean);
+ messages.add(null);
+ builders.add(builder);
+ onChanged();
+ incrementModCounts();
+ return builder.getBuilder();
+ }
+
+ /**
+ * Inserts a new builder at the specified position in this list.
+ * Shifts the element currently at that position (if any) and any subsequent
+ * elements to the right (adds one to their indices).
+ *
+ * @param index the index at which to insert the builder
+ * @param message the message to add which is the basis of the builder
+ * @return the builder
+ */
+ public BType addBuilder(int index, MType message) {
+ ensureMutableMessageList();
+ ensureBuilders();
+ SingleFieldBuilderV3<MType, BType, IType> builder =
+ new SingleFieldBuilderV3<MType, BType, IType>(
+ message, this, isClean);
+ messages.add(index, null);
+ builders.add(index, builder);
+ onChanged();
+ incrementModCounts();
+ return builder.getBuilder();
+ }
+
+ /**
+ * Removes the element at the specified position in this list. Shifts any
+ * subsequent elements to the left (subtracts one from their indices).
+ * Returns the element that was removed from the list.
+ *
+ * @param index the index at which to remove the message
+ */
+ public void remove(int index) {
+ ensureMutableMessageList();
+ messages.remove(index);
+ if (builders != null) {
+ SingleFieldBuilderV3<MType, BType, IType> entry =
+ builders.remove(index);
+ if (entry != null) {
+ entry.dispose();
+ }
+ }
+ onChanged();
+ incrementModCounts();
+ }
+
+ /**
+ * Removes all of the elements from this list.
+ * The list will be empty after this call returns.
+ */
+ public void clear() {
+ messages = Collections.emptyList();
+ isMessagesListMutable = false;
+ if (builders != null) {
+ for (SingleFieldBuilderV3<MType, BType, IType> entry :
+ builders) {
+ if (entry != null) {
+ entry.dispose();
+ }
+ }
+ builders = null;
+ }
+ onChanged();
+ incrementModCounts();
+ }
+
+ /**
+ * Builds the list of messages from the builder and returns them.
+ *
+ * @return an immutable list of messages
+ */
+ public List<MType> build() {
+ // Now that build has been called, we are required to dispatch
+ // invalidations.
+ isClean = true;
+
+ if (!isMessagesListMutable && builders == null) {
+ // We still have an immutable list and we never created a builder.
+ return messages;
+ }
+
+ boolean allMessagesInSync = true;
+ if (!isMessagesListMutable) {
+ // We still have an immutable list. Let's see if any of them are out
+ // of sync with their builders.
+ for (int i = 0; i < messages.size(); i++) {
+ Message message = messages.get(i);
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(i);
+ if (builder != null) {
+ if (builder.build() != message) {
+ allMessagesInSync = false;
+ break;
+ }
+ }
+ }
+ if (allMessagesInSync) {
+ // Immutable list is still in sync.
+ return messages;
+ }
+ }
+
+ // Need to make sure messages is up to date
+ ensureMutableMessageList();
+ for (int i = 0; i < messages.size(); i++) {
+ messages.set(i, getMessage(i, true));
+ }
+
+ // We're going to return our list as immutable so we mark that we can
+ // no longer update it.
+ messages = Collections.unmodifiableList(messages);
+ isMessagesListMutable = false;
+ return messages;
+ }
+
+ /**
+ * Gets a view of the builder as a list of messages. The returned list is live
+ * and will reflect any changes to the underlying builder.
+ *
+ * @return the messages in the list
+ */
+ public List<MType> getMessageList() {
+ if (externalMessageList == null) {
+ externalMessageList =
+ new MessageExternalList<MType, BType, IType>(this);
+ }
+ return externalMessageList;
+ }
+
+ /**
+ * Gets a view of the builder as a list of builders. This returned list is
+ * live and will reflect any changes to the underlying builder.
+ *
+ * @return the builders in the list
+ */
+ public List<BType> getBuilderList() {
+ if (externalBuilderList == null) {
+ externalBuilderList =
+ new BuilderExternalList<MType, BType, IType>(this);
+ }
+ return externalBuilderList;
+ }
+
+ /**
+ * Gets a view of the builder as a list of MessageOrBuilders. This returned
+ * list is live and will reflect any changes to the underlying builder.
+ *
+ * @return the builders in the list
+ */
+ public List<IType> getMessageOrBuilderList() {
+ if (externalMessageOrBuilderList == null) {
+ externalMessageOrBuilderList =
+ new MessageOrBuilderExternalList<MType, BType, IType>(this);
+ }
+ return externalMessageOrBuilderList;
+ }
+
+ /**
+ * Called when a the builder or one of its nested children has changed
+ * and any parent should be notified of its invalidation.
+ */
+ private void onChanged() {
+ if (isClean && parent != null) {
+ parent.markDirty();
+
+ // Don't keep dispatching invalidations until build is called again.
+ isClean = false;
+ }
+ }
+
+ @Override
+ public void markDirty() {
+ onChanged();
+ }
+
+ /**
+ * Increments the mod counts so that an ConcurrentModificationException can
+ * be thrown if calling code tries to modify the builder while its iterating
+ * the list.
+ */
+ private void incrementModCounts() {
+ if (externalMessageList != null) {
+ externalMessageList.incrementModCount();
+ }
+ if (externalBuilderList != null) {
+ externalBuilderList.incrementModCount();
+ }
+ if (externalMessageOrBuilderList != null) {
+ externalMessageOrBuilderList.incrementModCount();
+ }
+ }
+
+ /**
+ * Provides a live view of the builder as a list of messages.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ */
+ private static class MessageExternalList<
+ MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ extends AbstractList<MType> implements List<MType> {
+
+ RepeatedFieldBuilderV3<MType, BType, IType> builder;
+
+ MessageExternalList(
+ RepeatedFieldBuilderV3<MType, BType, IType> builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public int size() {
+ return this.builder.getCount();
+ }
+
+ @Override
+ public MType get(int index) {
+ return builder.getMessage(index);
+ }
+
+ void incrementModCount() {
+ modCount++;
+ }
+ }
+
+ /**
+ * Provides a live view of the builder as a list of builders.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ */
+ private static class BuilderExternalList<
+ MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ extends AbstractList<BType> implements List<BType> {
+
+ RepeatedFieldBuilderV3<MType, BType, IType> builder;
+
+ BuilderExternalList(
+ RepeatedFieldBuilderV3<MType, BType, IType> builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public int size() {
+ return this.builder.getCount();
+ }
+
+ @Override
+ public BType get(int index) {
+ return builder.getBuilder(index);
+ }
+
+ void incrementModCount() {
+ modCount++;
+ }
+ }
+
+ /**
+ * Provides a live view of the builder as a list of builders.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ */
+ private static class MessageOrBuilderExternalList<
+ MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ extends AbstractList<IType> implements List<IType> {
+
+ RepeatedFieldBuilderV3<MType, BType, IType> builder;
+
+ MessageOrBuilderExternalList(
+ RepeatedFieldBuilderV3<MType, BType, IType> builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public int size() {
+ return this.builder.getCount();
+ }
+
+ @Override
+ public IType get(int index) {
+ return builder.getMessageOrBuilder(index);
+ }
+
+ void incrementModCount() {
+ modCount++;
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java
new file mode 100644
index 00000000..fb1f76a7
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java
@@ -0,0 +1,241 @@
+// 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;
+
+/**
+ * {@code SingleFieldBuilderV3} implements a structure that a protocol
+ * message uses to hold a single field of another protocol message. It supports
+ * the classical use case of setting an immutable {@link Message} as the value
+ * of the field and is highly optimized around this.
+ * <br>
+ * It also supports the additional use case of setting a {@link Message.Builder}
+ * as the field and deferring conversion of that {@code Builder}
+ * to an immutable {@code Message}. In this way, it's possible to maintain
+ * a tree of {@code Builder}'s that acts as a fully read/write data
+ * structure.
+ * <br>
+ * Logically, one can think of a tree of builders as converting the entire tree
+ * to messages when build is called on the root or when any method is called
+ * that desires a Message instead of a Builder. In terms of the implementation,
+ * the {@code SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3}
+ * classes cache messages that were created so that messages only need to be
+ * created when some change occurred in its builder or a builder for one of its
+ * descendants.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ *
+ * @author jonp@google.com (Jon Perlow)
+ */
+public class SingleFieldBuilderV3
+ <MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ implements AbstractMessage.BuilderParent {
+
+ // Parent to send changes to.
+ private AbstractMessage.BuilderParent parent;
+
+ // Invariant: one of builder or message fields must be non-null.
+
+ // If set, this is the case where we are backed by a builder. In this case,
+ // message field represents a cached message for the builder (or null if
+ // there is no cached message).
+ private BType builder;
+
+ // If builder is non-null, this represents a cached message from the builder.
+ // If builder is null, this is the authoritative message for the field.
+ private MType message;
+
+ // Indicates that we've built a message and so we are now obligated
+ // to dispatch dirty invalidations. See AbstractMessage.BuilderListener.
+ private boolean isClean;
+
+ public SingleFieldBuilderV3(
+ MType message,
+ AbstractMessage.BuilderParent parent,
+ boolean isClean) {
+ if (message == null) {
+ throw new NullPointerException();
+ }
+ this.message = message;
+ this.parent = parent;
+ this.isClean = isClean;
+ }
+
+ public void dispose() {
+ // Null out parent so we stop sending it invalidations.
+ parent = null;
+ }
+
+ /**
+ * Get the message for the field. If the message is currently stored
+ * as a {@code Builder}, it is converted to a {@code Message} by
+ * calling {@link Message.Builder#buildPartial} on it. If no message has
+ * been set, returns the default instance of the message.
+ *
+ * @return the message for the field
+ */
+ @SuppressWarnings("unchecked")
+ public MType getMessage() {
+ if (message == null) {
+ // If message is null, the invariant is that we must be have a builder.
+ message = (MType) builder.buildPartial();
+ }
+ return message;
+ }
+
+ /**
+ * Builds the message and returns it.
+ *
+ * @return the message
+ */
+ public MType build() {
+ // Now that build has been called, we are required to dispatch
+ // invalidations.
+ isClean = true;
+ return getMessage();
+ }
+
+ /**
+ * Gets a builder for the field. If no builder has been created yet, a
+ * builder is created on demand by calling {@link Message#toBuilder}.
+ *
+ * @return The builder for the field
+ */
+ @SuppressWarnings("unchecked")
+ public BType getBuilder() {
+ if (builder == null) {
+ // builder.mergeFrom() on a fresh builder
+ // does not create any sub-objects with independent clean/dirty states,
+ // therefore setting the builder itself to clean without actually calling
+ // build() cannot break any invariants.
+ builder = (BType) message.newBuilderForType(this);
+ builder.mergeFrom(message); // no-op if message is the default message
+ builder.markClean();
+ }
+ return builder;
+ }
+
+ /**
+ * Gets the base class interface for the field. This may either be a builder
+ * or a message. It will return whatever is more efficient.
+ *
+ * @return the message or builder for the field as the base class interface
+ */
+ @SuppressWarnings("unchecked")
+ public IType getMessageOrBuilder() {
+ if (builder != null) {
+ return (IType) builder;
+ } else {
+ return (IType) message;
+ }
+ }
+
+ /**
+ * Sets a message for the field replacing any existing value.
+ *
+ * @param message the message to set
+ * @return the builder
+ */
+ public SingleFieldBuilderV3<MType, BType, IType> setMessage(
+ MType message) {
+ if (message == null) {
+ throw new NullPointerException();
+ }
+ this.message = message;
+ if (builder != null) {
+ builder.dispose();
+ builder = null;
+ }
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Merges the field from another field.
+ *
+ * @param value the value to merge from
+ * @return the builder
+ */
+ public SingleFieldBuilderV3<MType, BType, IType> mergeFrom(
+ MType value) {
+ if (builder == null && message == message.getDefaultInstanceForType()) {
+ message = value;
+ } else {
+ getBuilder().mergeFrom(value);
+ }
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Clears the value of the field.
+ *
+ * @return the builder
+ */
+ @SuppressWarnings("unchecked")
+ public SingleFieldBuilderV3<MType, BType, IType> clear() {
+ message = (MType) (message != null ?
+ message.getDefaultInstanceForType() :
+ builder.getDefaultInstanceForType());
+ if (builder != null) {
+ builder.dispose();
+ builder = null;
+ }
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Called when a the builder or one of its nested children has changed
+ * and any parent should be notified of its invalidation.
+ */
+ private void onChanged() {
+ // If builder is null, this is the case where onChanged is being called
+ // from setMessage or clear.
+ if (builder != null) {
+ message = null;
+ }
+ if (isClean && parent != null) {
+ parent.markDirty();
+
+ // Don't keep dispatching invalidations until build is called again.
+ isClean = false;
+ }
+ }
+
+ @Override
+ public void markDirty() {
+ onChanged();
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java
index c1c328fc..ff13675d 100644
--- a/java/core/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -661,6 +661,14 @@ public final class TextFormat {
nextToken();
}
+ int getPreviousLine() {
+ return previousLine;
+ }
+
+ int getPreviousColumn() {
+ return previousColumn;
+ }
+
int getLine() {
return line;
}
@@ -1374,6 +1382,28 @@ public final class TextFormat {
return text;
}
+ // Check both unknown fields and unknown extensions and log warming messages
+ // or throw exceptions according to the flag.
+ private void checkUnknownFields(final List<String> unknownFields)
+ throws ParseException {
+ if (unknownFields.isEmpty()) {
+ return;
+ }
+
+ StringBuilder msg = new StringBuilder("Input contains unknown fields and/or extensions:");
+ for (String field : unknownFields) {
+ msg.append('\n').append(field);
+ }
+
+ if (allowUnknownFields) {
+ logger.warning(msg.toString());
+ } else {
+ String[] lineColumn = unknownFields.get(0).split(":");
+ throw new ParseException(Integer.valueOf(lineColumn[0]),
+ Integer.valueOf(lineColumn[1]), msg.toString());
+ }
+ }
+
/**
* Parse a text-format message from {@code input} and merge the contents
* into {@code builder}. Extensions will be recognized if they are
@@ -1387,9 +1417,13 @@ public final class TextFormat {
MessageReflection.BuilderAdapter target =
new MessageReflection.BuilderAdapter(builder);
+ List<String> unknownFields = new ArrayList<String>();
+
while (!tokenizer.atEnd()) {
- mergeField(tokenizer, extensionRegistry, target);
+ mergeField(tokenizer, extensionRegistry, target, unknownFields);
}
+
+ checkUnknownFields(unknownFields);
}
@@ -1399,9 +1433,11 @@ public final class TextFormat {
*/
private void mergeField(final Tokenizer tokenizer,
final ExtensionRegistry extensionRegistry,
- final MessageReflection.MergeTarget target)
+ final MessageReflection.MergeTarget target,
+ List<String> unknownFields)
throws ParseException {
- mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder);
+ mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder,
+ unknownFields);
}
/**
@@ -1411,7 +1447,8 @@ public final class TextFormat {
private void mergeField(final Tokenizer tokenizer,
final ExtensionRegistry extensionRegistry,
final MessageReflection.MergeTarget target,
- TextFormatParseInfoTree.Builder parseTreeBuilder)
+ TextFormatParseInfoTree.Builder parseTreeBuilder,
+ List<String> unknownFields)
throws ParseException {
FieldDescriptor field = null;
int startLine = tokenizer.getLine();
@@ -1432,13 +1469,9 @@ public final class TextFormat {
extensionRegistry, name.toString());
if (extension == null) {
- if (!allowUnknownFields) {
- throw tokenizer.parseExceptionPreviousToken(
- "Extension \"" + name + "\" not found in the ExtensionRegistry.");
- } else {
- logger.warning(
- "Extension \"" + name + "\" not found in the ExtensionRegistry.");
- }
+ unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
+ (tokenizer.getPreviousColumn() + 1) + ":\t" +
+ type.getFullName() + ".[" + name + "]");
} else {
if (extension.descriptor.getContainingType() != type) {
throw tokenizer.parseExceptionPreviousToken(
@@ -1473,16 +1506,9 @@ public final class TextFormat {
}
if (field == null) {
- if (!allowUnknownFields) {
- throw tokenizer.unknownFieldParseExceptionPreviousToken(
- name,
- "Message type \"" + type.getFullName()
- + "\" has no field named \"" + name + "\".");
- } else {
- logger.warning(
- "Message type \"" + type.getFullName()
- + "\" has no field named \"" + name + "\".");
- }
+ unknownFields.add((tokenizer.getPreviousLine() + 1) + ":" +
+ (tokenizer.getPreviousColumn() + 1) + ":\t" +
+ type.getFullName() + "." + name);
}
}
@@ -1511,15 +1537,15 @@ public final class TextFormat {
TextFormatParseInfoTree.Builder childParseTreeBuilder =
parseTreeBuilder.getBuilderForSubMessageField(field);
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
- childParseTreeBuilder);
+ childParseTreeBuilder, unknownFields);
} else {
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
- parseTreeBuilder);
+ parseTreeBuilder, unknownFields);
}
} else {
tokenizer.consume(":"); // required
- consumeFieldValues(
- tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder);
+ consumeFieldValues(tokenizer, extensionRegistry, target, field,
+ extension, parseTreeBuilder, unknownFields);
}
if (parseTreeBuilder != null) {
@@ -1544,14 +1570,15 @@ public final class TextFormat {
final MessageReflection.MergeTarget target,
final FieldDescriptor field,
final ExtensionRegistry.ExtensionInfo extension,
- final TextFormatParseInfoTree.Builder parseTreeBuilder)
+ final TextFormatParseInfoTree.Builder parseTreeBuilder,
+ List<String> unknownFields)
throws ParseException {
// Support specifying repeated field values as a comma-separated list.
// Ex."foo: [1, 2, 3]"
if (field.isRepeated() && tokenizer.tryConsume("[")) {
while (true) {
consumeFieldValue(tokenizer, extensionRegistry, target, field, extension,
- parseTreeBuilder);
+ parseTreeBuilder, unknownFields);
if (tokenizer.tryConsume("]")) {
// End of list.
break;
@@ -1559,8 +1586,8 @@ public final class TextFormat {
tokenizer.consume(",");
}
} else {
- consumeFieldValue(
- tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder);
+ consumeFieldValue(tokenizer, extensionRegistry, target, field,
+ extension, parseTreeBuilder, unknownFields);
}
}
@@ -1574,7 +1601,8 @@ public final class TextFormat {
final MessageReflection.MergeTarget target,
final FieldDescriptor field,
final ExtensionRegistry.ExtensionInfo extension,
- final TextFormatParseInfoTree.Builder parseTreeBuilder)
+ final TextFormatParseInfoTree.Builder parseTreeBuilder,
+ List<String> unknownFields)
throws ParseException {
Object value = null;
@@ -1596,7 +1624,8 @@ public final class TextFormat {
throw tokenizer.parseException(
"Expected \"" + endToken + "\".");
}
- mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder);
+ mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder,
+ unknownFields);
}
value = subField.finish();
diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
index c906420d..6d33d3a8 100644
--- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
@@ -57,6 +57,7 @@ import java.util.TreeMap;
* @author kenton@google.com Kenton Varda
*/
public final class UnknownFieldSet implements MessageLite {
+
private UnknownFieldSet() {}
/** Create a new {@link Builder}. */
@@ -130,7 +131,8 @@ public final class UnknownFieldSet implements MessageLite {
@Override
public void writeTo(final CodedOutputStream output) throws IOException {
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
- entry.getValue().writeTo(entry.getKey(), output);
+ Field field = entry.getValue();
+ field.writeTo(entry.getKey(), output);
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
index 0fbf4d40..e72a6b4d 100644
--- a/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
+++ b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
@@ -42,6 +42,23 @@ import java.nio.ByteBuffer;
* guaranteed that the buffer backing the {@link ByteString} will never change! Mutation of a
* {@link ByteString} can lead to unexpected and undesirable consequences in your application,
* and will likely be difficult to debug. Proceed with caution!
+ *
+ * <p>This can have a number of significant side affects that have
+ * spooky-action-at-a-distance-like behavior. In particular, if the bytes value changes out from
+ * under a Protocol Buffer:
+ * <ul>
+ * <li>serialization may throw
+ * <li>serialization may succeed but the wrong bytes may be written out
+ * <li>messages are no longer threadsafe
+ * <li>hashCode may be incorrect
+ * <ul>
+ * <li>can result in a permanent memory leak when used as a key in a long-lived HashMap
+ * <li> the semantics of many programs may be violated if this is the case
+ * </ul>
+ * </ul>
+ * Each of these issues will occur in parts of the code base that are entirely distinct from the
+ * parts of the code base modifying the buffer. In fact, both parts of the code base may be correct
+ * - it is the bridging with the unsafe operations that was in error!
*/
@ExperimentalApi
public final class UnsafeByteOperations {
diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java
new file mode 100644
index 00000000..6a4787d1
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java
@@ -0,0 +1,210 @@
+// 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 sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * Utility class for working with unsafe operations.
+ */
+// TODO(nathanmittler): Add support for Android Memory/MemoryBlock
+final class UnsafeUtil {
+ private static final sun.misc.Unsafe UNSAFE = getUnsafe();
+ private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS =
+ supportsUnsafeByteBufferOperations();
+ private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations();
+ private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset();
+ private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(field(Buffer.class, "address"));
+
+ private UnsafeUtil() {
+ }
+
+ static boolean hasUnsafeArrayOperations() {
+ return HAS_UNSAFE_ARRAY_OPERATIONS;
+ }
+
+ static boolean hasUnsafeByteBufferOperations() {
+ return HAS_UNSAFE_BYTEBUFFER_OPERATIONS;
+ }
+
+ static long getArrayBaseOffset() {
+ return ARRAY_BASE_OFFSET;
+ }
+
+ static byte getByte(byte[] target, long offset) {
+ return UNSAFE.getByte(target, offset);
+ }
+
+ static void putByte(byte[] target, long offset, byte value) {
+ UNSAFE.putByte(target, offset, value);
+ }
+
+ static void copyMemory(
+ byte[] src, long srcOffset, byte[] target, long targetOffset, long length) {
+ UNSAFE.copyMemory(src, srcOffset, target, targetOffset, length);
+ }
+
+ static long getLong(byte[] target, long offset) {
+ return UNSAFE.getLong(target, offset);
+ }
+
+ static byte getByte(long address) {
+ return UNSAFE.getByte(address);
+ }
+
+ static void putByte(long address, byte value) {
+ UNSAFE.putByte(address, value);
+ }
+
+ static long getLong(long address) {
+ return UNSAFE.getLong(address);
+ }
+
+ static void copyMemory(long srcAddress, long targetAddress, long length) {
+ UNSAFE.copyMemory(srcAddress, targetAddress, length);
+ }
+
+ /**
+ * Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}.
+ */
+ static long addressOffset(ByteBuffer buffer) {
+ return UNSAFE.getLong(buffer, BUFFER_ADDRESS_OFFSET);
+ }
+
+ /**
+ * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this platform.
+ */
+ private static sun.misc.Unsafe getUnsafe() {
+ sun.misc.Unsafe unsafe = null;
+ try {
+ unsafe =
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Unsafe>() {
+ @Override
+ public sun.misc.Unsafe run() throws Exception {
+ Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
+
+ for (Field f : k.getDeclaredFields()) {
+ f.setAccessible(true);
+ Object x = f.get(null);
+ if (k.isInstance(x)) {
+ return k.cast(x);
+ }
+ }
+ // The sun.misc.Unsafe field does not exist.
+ return null;
+ }
+ });
+ } catch (Throwable e) {
+ // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
+ // for Unsafe.
+ }
+ return unsafe;
+ }
+
+ /**
+ * Indicates whether or not unsafe array operations are supported on this platform.
+ */
+ private static boolean supportsUnsafeArrayOperations() {
+ boolean supported = false;
+ if (UNSAFE != null) {
+ try {
+ Class<?> clazz = UNSAFE.getClass();
+ clazz.getMethod("arrayBaseOffset", Class.class);
+ clazz.getMethod("getByte", Object.class, long.class);
+ clazz.getMethod("putByte", Object.class, long.class, byte.class);
+ clazz.getMethod("getLong", Object.class, long.class);
+ clazz.getMethod(
+ "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
+ supported = true;
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ }
+ return supported;
+ }
+
+ private static boolean supportsUnsafeByteBufferOperations() {
+ boolean supported = false;
+ if (UNSAFE != null) {
+ try {
+ Class<?> clazz = UNSAFE.getClass();
+ clazz.getMethod("objectFieldOffset", Field.class);
+ clazz.getMethod("getByte", long.class);
+ clazz.getMethod("getLong", Object.class, long.class);
+ clazz.getMethod("putByte", long.class, byte.class);
+ clazz.getMethod("getLong", long.class);
+ clazz.getMethod("copyMemory", long.class, long.class, long.class);
+ supported = true;
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ }
+ return supported;
+ }
+
+ /**
+ * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not available.
+ */
+ private static int byteArrayBaseOffset() {
+ return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1;
+ }
+
+ /**
+ * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
+ * available.
+ */
+ private static long fieldOffset(Field field) {
+ return field == null || UNSAFE == null ? -1 : UNSAFE.objectFieldOffset(field);
+ }
+
+ /**
+ * Gets the field with the given name within the class, or {@code null} if not found. If found,
+ * the field is made accessible.
+ */
+ private static Field field(Class<?> clazz, String fieldName) {
+ Field field;
+ try {
+ field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ } catch (Throwable t) {
+ // Failed to access the fields.
+ field = null;
+ }
+ return field;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Utf8.java b/java/core/src/main/java/com/google/protobuf/Utf8.java
index 308c69e9..5b80d405 100644
--- a/java/core/src/main/java/com/google/protobuf/Utf8.java
+++ b/java/core/src/main/java/com/google/protobuf/Utf8.java
@@ -30,18 +30,16 @@
package com.google.protobuf;
+import static com.google.protobuf.UnsafeUtil.addressOffset;
+import static com.google.protobuf.UnsafeUtil.getArrayBaseOffset;
+import static com.google.protobuf.UnsafeUtil.hasUnsafeArrayOperations;
+import static com.google.protobuf.UnsafeUtil.hasUnsafeByteBufferOperations;
import static java.lang.Character.MAX_SURROGATE;
import static java.lang.Character.MIN_SURROGATE;
import static java.lang.Character.isSurrogatePair;
import static java.lang.Character.toCodePoint;
-import java.lang.reflect.Field;
-import java.nio.Buffer;
import java.nio.ByteBuffer;
-import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
-import java.util.logging.Level;
-import java.util.logging.Logger;
/**
* A set of low-level, high-performance static utility methods related
@@ -79,7 +77,6 @@ import java.util.logging.Logger;
*/
// TODO(nathanmittler): Copy changes in this class back to Guava
final class Utf8 {
- private static final Logger logger = Logger.getLogger(Utf8.class.getName());
/**
* UTF-8 is a runtime hot spot so we attempt to provide heavily optimized implementations
@@ -237,7 +234,7 @@ final class Utf8 {
// fallback to more lenient behavior.
static class UnpairedSurrogateException extends IllegalArgumentException {
- private UnpairedSurrogateException(int index, int length) {
+ UnpairedSurrogateException(int index, int length) {
super("Unpaired surrogate at index " + index + " of " + length);
}
}
@@ -991,23 +988,11 @@ final class Utf8 {
* {@link Processor} that uses {@code sun.misc.Unsafe} where possible to improve performance.
*/
static final class UnsafeProcessor extends Processor {
- private static final sun.misc.Unsafe UNSAFE = getUnsafe();
- private static final long BUFFER_ADDRESS_OFFSET =
- fieldOffset(field(Buffer.class, "address"));
- private static final int ARRAY_BASE_OFFSET = byteArrayBaseOffset();
-
- /**
- * We only use Unsafe operations if we have access to direct {@link ByteBuffer}'s address
- * and the array base offset is a multiple of 8 (needed by Unsafe.getLong()).
- */
- private static final boolean AVAILABLE =
- BUFFER_ADDRESS_OFFSET != -1 && ARRAY_BASE_OFFSET % 8 == 0;
-
/**
* Indicates whether or not all required unsafe operations are supported on this platform.
*/
static boolean isAvailable() {
- return AVAILABLE;
+ return hasUnsafeArrayOperations() && hasUnsafeByteBufferOperations();
}
@Override
@@ -1016,8 +1001,8 @@ final class Utf8 {
throw new ArrayIndexOutOfBoundsException(
String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit));
}
- long offset = ARRAY_BASE_OFFSET + index;
- final long offsetLimit = ARRAY_BASE_OFFSET + limit;
+ long offset = getArrayBaseOffset() + index;
+ final long offsetLimit = getArrayBaseOffset() + limit;
if (state != COMPLETE) {
// The previous decoding operation was incomplete (or malformed).
// We look for a well-formed sequence consisting of bytes from
@@ -1038,7 +1023,7 @@ final class Utf8 {
// leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2
// byte2 trailing-byte test
- || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else if (byte1 < (byte) 0xF0) {
@@ -1047,7 +1032,7 @@ final class Utf8 {
// Get byte2 from saved state or array
int byte2 = (byte) ~(state >> 8);
if (byte2 == 0) {
- byte2 = UNSAFE.getByte(bytes, offset++);
+ byte2 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2);
}
@@ -1058,7 +1043,7 @@ final class Utf8 {
// illegal surrogate codepoint?
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
- || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@@ -1068,7 +1053,7 @@ final class Utf8 {
int byte2 = (byte) ~(state >> 8);
int byte3 = 0;
if (byte2 == 0) {
- byte2 = UNSAFE.getByte(bytes, offset++);
+ byte2 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2);
}
@@ -1076,7 +1061,7 @@ final class Utf8 {
byte3 = (byte) (state >> 16);
}
if (byte3 == 0) {
- byte3 = UNSAFE.getByte(bytes, offset++);
+ byte3 = UnsafeUtil.getByte(bytes, offset++);
if (offset >= offsetLimit) {
return incompleteStateFor(byte1, byte2, byte3);
}
@@ -1095,7 +1080,7 @@ final class Utf8 {
// byte3 trailing-byte test
|| byte3 > (byte) 0xBF
// byte4 trailing-byte test
- || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
}
@@ -1134,7 +1119,7 @@ final class Utf8 {
// leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2
// byte2 trailing-byte test
- || UNSAFE.getByte(address++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
} else if (byte1 < (byte) 0xF0) {
@@ -1143,7 +1128,7 @@ final class Utf8 {
// Get byte2 from saved state or array
int byte2 = (byte) ~(state >> 8);
if (byte2 == 0) {
- byte2 = UNSAFE.getByte(address++);
+ byte2 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2);
}
@@ -1154,7 +1139,7 @@ final class Utf8 {
// illegal surrogate codepoint?
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
- || UNSAFE.getByte(address++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@@ -1164,7 +1149,7 @@ final class Utf8 {
int byte2 = (byte) ~(state >> 8);
int byte3 = 0;
if (byte2 == 0) {
- byte2 = UNSAFE.getByte(address++);
+ byte2 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2);
}
@@ -1172,7 +1157,7 @@ final class Utf8 {
byte3 = (byte) (state >> 16);
}
if (byte3 == 0) {
- byte3 = UNSAFE.getByte(address++);
+ byte3 = UnsafeUtil.getByte(address++);
if (address >= addressLimit) {
return incompleteStateFor(byte1, byte2, byte3);
}
@@ -1191,7 +1176,7 @@ final class Utf8 {
// byte3 trailing-byte test
|| byte3 > (byte) 0xBF
// byte4 trailing-byte test
- || UNSAFE.getByte(address++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
}
@@ -1202,7 +1187,7 @@ final class Utf8 {
@Override
int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) {
- long outIx = ARRAY_BASE_OFFSET + offset;
+ long outIx = getArrayBaseOffset() + offset;
final long outLimit = outIx + length;
final int inLimit = in.length();
if (inLimit > length || out.length - length < offset) {
@@ -1215,25 +1200,25 @@ final class Utf8 {
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
int inIx = 0;
for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) {
- UNSAFE.putByte(out, outIx++, (byte) c);
+ UnsafeUtil.putByte(out, outIx++, (byte) c);
}
if (inIx == inLimit) {
// We're done, it was ASCII encoded.
- return (int) (outIx - ARRAY_BASE_OFFSET);
+ return (int) (outIx - getArrayBaseOffset());
}
for (char c; inIx < inLimit; ++inIx) {
c = in.charAt(inIx);
if (c < 0x80 && outIx < outLimit) {
- UNSAFE.putByte(out, outIx++, (byte) c);
+ UnsafeUtil.putByte(out, outIx++, (byte) c);
} else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes
- UNSAFE.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6)));
- UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6)));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
} else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
- UNSAFE.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12)));
- UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
- UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12)));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
} else if (outIx <= outLimit - 4L) {
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
// bytes
@@ -1242,10 +1227,10 @@ final class Utf8 {
throw new UnpairedSurrogateException((inIx - 1), inLimit);
}
int codePoint = toCodePoint(c, low);
- UNSAFE.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
- UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
- UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
- UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint)));
+ UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint)));
} else {
if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
&& (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
@@ -1258,7 +1243,7 @@ final class Utf8 {
}
// All bytes have been encoded.
- return (int) (outIx - ARRAY_BASE_OFFSET);
+ return (int) (outIx - getArrayBaseOffset());
}
@Override
@@ -1277,7 +1262,7 @@ final class Utf8 {
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
int inIx = 0;
for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) {
- UNSAFE.putByte(outIx++, (byte) c);
+ UnsafeUtil.putByte(outIx++, (byte) c);
}
if (inIx == inLimit) {
// We're done, it was ASCII encoded.
@@ -1288,15 +1273,15 @@ final class Utf8 {
for (char c; inIx < inLimit; ++inIx) {
c = in.charAt(inIx);
if (c < 0x80 && outIx < outLimit) {
- UNSAFE.putByte(outIx++, (byte) c);
+ UnsafeUtil.putByte(outIx++, (byte) c);
} else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes
- UNSAFE.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6)));
- UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6)));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
} else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
- UNSAFE.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12)));
- UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
- UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12)));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
} else if (outIx <= outLimit - 4L) {
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
// bytes
@@ -1305,10 +1290,10 @@ final class Utf8 {
throw new UnpairedSurrogateException((inIx - 1), inLimit);
}
int codePoint = toCodePoint(c, low);
- UNSAFE.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
- UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
- UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
- UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint)));
+ UnsafeUtil.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint)));
} else {
if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
&& (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
@@ -1349,7 +1334,7 @@ final class Utf8 {
// we're 8-byte aligned.
final int unaligned = (int) offset & 7;
for (int j = unaligned; j > 0; j--) {
- if (UNSAFE.getByte(bytes, offset++) < 0) {
+ if (UnsafeUtil.getByte(bytes, offset++) < 0) {
return unaligned - j;
}
}
@@ -1358,7 +1343,7 @@ final class Utf8 {
// To speed things up further, we're reading longs instead of bytes so we use a mask to
// determine if any byte in the current long is non-ASCII.
remaining -= unaligned;
- for (; remaining >= 8 && (UNSAFE.getLong(bytes, offset) & ASCII_MASK_LONG) == 0;
+ for (; remaining >= 8 && (UnsafeUtil.getLong(bytes, offset) & ASCII_MASK_LONG) == 0;
offset += 8, remaining -= 8) {}
return maxChars - remaining;
}
@@ -1379,7 +1364,7 @@ final class Utf8 {
// be read before we're 8-byte aligned.
final int unaligned = (int) address & 7;
for (int j = unaligned; j > 0; j--) {
- if (UNSAFE.getByte(address++) < 0) {
+ if (UnsafeUtil.getByte(address++) < 0) {
return unaligned - j;
}
}
@@ -1388,7 +1373,7 @@ final class Utf8 {
// To speed things up further, we're reading longs instead of bytes so we use a mask to
// determine if any byte in the current long is non-ASCII.
remaining -= unaligned;
- for (; remaining >= 8 && (UNSAFE.getLong(address) & ASCII_MASK_LONG) == 0;
+ for (; remaining >= 8 && (UnsafeUtil.getLong(address) & ASCII_MASK_LONG) == 0;
address += 8, remaining -= 8) {}
return maxChars - remaining;
}
@@ -1404,7 +1389,7 @@ final class Utf8 {
// TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold?
// Maybe after seeing a few in a row that are ASCII, go back to fast mode?
int byte1 = 0;
- for (; remaining > 0 && (byte1 = UNSAFE.getByte(bytes, offset++)) >= 0; --remaining) {
+ for (; remaining > 0 && (byte1 = UnsafeUtil.getByte(bytes, offset++)) >= 0; --remaining) {
}
if (remaining == 0) {
return COMPLETE;
@@ -1423,7 +1408,7 @@ final class Utf8 {
// Simultaneously checks for illegal trailing-byte in
// leading position and overlong 2-byte form.
if (byte1 < (byte) 0xC2
- || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else if (byte1 < (byte) 0xF0) {
@@ -1435,13 +1420,13 @@ final class Utf8 {
remaining -= 2;
final int byte2;
- if ((byte2 = UNSAFE.getByte(bytes, offset++)) > (byte) 0xBF
+ if ((byte2 = UnsafeUtil.getByte(bytes, offset++)) > (byte) 0xBF
// overlong? 5 most significant bits must not all be zero
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
// check for illegal surrogate codepoints
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
- || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@@ -1453,16 +1438,16 @@ final class Utf8 {
remaining -= 3;
final int byte2;
- if ((byte2 = UNSAFE.getByte(bytes, offset++)) > (byte) 0xBF
+ if ((byte2 = UnsafeUtil.getByte(bytes, offset++)) > (byte) 0xBF
// Check that 1 <= plane <= 16. Tricky optimized form of:
// if (byte1 > (byte) 0xF4 ||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
// byte3 trailing-byte test
- || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF
// byte4 trailing-byte test
- || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
return MALFORMED;
}
}
@@ -1480,7 +1465,7 @@ final class Utf8 {
// TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold?
// Maybe after seeing a few in a row that are ASCII, go back to fast mode?
int byte1 = 0;
- for (; remaining > 0 && (byte1 = UNSAFE.getByte(address++)) >= 0; --remaining) {
+ for (; remaining > 0 && (byte1 = UnsafeUtil.getByte(address++)) >= 0; --remaining) {
}
if (remaining == 0) {
return COMPLETE;
@@ -1498,7 +1483,7 @@ final class Utf8 {
// Simultaneously checks for illegal trailing-byte in
// leading position and overlong 2-byte form.
- if (byte1 < (byte) 0xC2 || UNSAFE.getByte(address++) > (byte) 0xBF) {
+ if (byte1 < (byte) 0xC2 || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
} else if (byte1 < (byte) 0xF0) {
@@ -1510,14 +1495,14 @@ final class Utf8 {
}
remaining -= 2;
- final byte byte2 = UNSAFE.getByte(address++);
+ final byte byte2 = UnsafeUtil.getByte(address++);
if (byte2 > (byte) 0xBF
// overlong? 5 most significant bits must not all be zero
|| (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
// check for illegal surrogate codepoints
|| (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
// byte3 trailing-byte test
- || UNSAFE.getByte(address++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
} else {
@@ -1529,7 +1514,7 @@ final class Utf8 {
}
remaining -= 3;
- final byte byte2 = UNSAFE.getByte(address++);
+ final byte byte2 = UnsafeUtil.getByte(address++);
if (byte2 > (byte) 0xBF
// Check that 1 <= plane <= 16. Tricky optimized form of:
// if (byte1 > (byte) 0xF4 ||
@@ -1537,9 +1522,9 @@ final class Utf8 {
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|| (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
// byte3 trailing-byte test
- || UNSAFE.getByte(address++) > (byte) 0xBF
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF
// byte4 trailing-byte test
- || UNSAFE.getByte(address++) > (byte) 0xBF) {
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
return MALFORMED;
}
}
@@ -1553,11 +1538,11 @@ final class Utf8 {
return incompleteStateFor(byte1);
}
case 1: {
- return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset));
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset));
}
case 2: {
- return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset),
- UNSAFE.getByte(bytes, offset + 1));
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset),
+ UnsafeUtil.getByte(bytes, offset + 1));
}
default: {
throw new AssertionError();
@@ -1571,112 +1556,17 @@ final class Utf8 {
return incompleteStateFor(byte1);
}
case 1: {
- return incompleteStateFor(byte1, UNSAFE.getByte(address));
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(address));
}
case 2: {
- return incompleteStateFor(byte1, UNSAFE.getByte(address), UNSAFE.getByte(address + 1));
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(address),
+ UnsafeUtil.getByte(address + 1));
}
default: {
throw new AssertionError();
}
}
}
-
- /**
- * Gets the field with the given name within the class, or {@code null} if not found. If
- * found, the field is made accessible.
- */
- private static Field field(Class<?> clazz, String fieldName) {
- Field field;
- try {
- field = clazz.getDeclaredField(fieldName);
- field.setAccessible(true);
- } catch (Throwable t) {
- // Failed to access the fields.
- field = null;
- }
- logger.log(Level.FINEST, "{0}.{1}: {2}",
- new Object[] {clazz.getName(), fieldName, (field != null ? "available" : "unavailable")});
- return field;
- }
-
- /**
- * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
- * available.
- */
- private static long fieldOffset(Field field) {
- return field == null || UNSAFE == null ? -1 : UNSAFE.objectFieldOffset(field);
- }
-
- /**
- * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not
- * available.
- */
- private static <T> int byteArrayBaseOffset() {
- return UNSAFE == null ? -1 : UNSAFE.arrayBaseOffset(byte[].class);
- }
-
- /**
- * Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}.
- */
- private static long addressOffset(ByteBuffer buffer) {
- return UNSAFE.getLong(buffer, BUFFER_ADDRESS_OFFSET);
- }
-
- /**
- * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this
- * platform.
- */
- private static sun.misc.Unsafe getUnsafe() {
- sun.misc.Unsafe unsafe = null;
- try {
- unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<sun.misc.Unsafe>() {
- @Override
- public sun.misc.Unsafe run() throws Exception {
- Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
-
- // Check that this platform supports all of the required unsafe methods.
- checkRequiredMethods(k);
-
- for (Field f : k.getDeclaredFields()) {
- f.setAccessible(true);
- Object x = f.get(null);
- if (k.isInstance(x)) {
- return k.cast(x);
- }
- }
- // The sun.misc.Unsafe field does not exist.
- return null;
- }
- });
- } catch (Throwable e) {
- // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
- // for Unsafe.
- }
-
- logger.log(Level.FINEST, "sun.misc.Unsafe: {}",
- unsafe != null ? "available" : "unavailable");
- return unsafe;
- }
-
- /**
- * Verifies that all required methods of {@code sun.misc.Unsafe} are available on this platform.
- */
- private static void checkRequiredMethods(Class<sun.misc.Unsafe> clazz)
- throws NoSuchMethodException, SecurityException {
- // Needed for Unsafe byte[] access
- clazz.getMethod("arrayBaseOffset", Class.class);
- clazz.getMethod("getByte", Object.class, long.class);
- clazz.getMethod("putByte", Object.class, long.class, byte.class);
- clazz.getMethod("getLong", Object.class, long.class);
-
- // Needed for Unsafe Direct ByteBuffer access
- clazz.getMethod("objectFieldOffset", Field.class);
- clazz.getMethod("getByte", long.class);
- clazz.getMethod("getLong", Object.class, long.class);
- clazz.getMethod("putByte", long.class, byte.class);
- clazz.getMethod("getLong", long.class);
- }
}
private Utf8() {}