summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitsuru Oshima <oshima@google.com>2009-06-10 16:31:48 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-06-10 16:31:48 -0700
commitf958768159817dba6d01befa2fd70786e3c1ea26 (patch)
tree1f1a5ba1039a0ab62dac27735c0ecc23dc11d9c5
parente1db06285718a64bb5000c88512bb35545aa7b4d (diff)
parent7fea449b53848057686ac4266ee51853bda70e30 (diff)
downloadexternal_protobuf-f958768159817dba6d01befa2fd70786e3c1ea26.zip
external_protobuf-f958768159817dba6d01befa2fd70786e3c1ea26.tar.gz
external_protobuf-f958768159817dba6d01befa2fd70786e3c1ea26.tar.bz2
am 7fea449b: Revert "ProtoBuf update"
Merge commit '7fea449b53848057686ac4266ee51853bda70e30' * commit '7fea449b53848057686ac4266ee51853bda70e30': Revert "ProtoBuf update"
-rw-r--r--src/com/google/common/io/protocol/IntMap.java383
-rw-r--r--src/com/google/common/io/protocol/ProtoBuf.java316
-rw-r--r--src/com/google/common/io/protocol/ProtoBufType.java106
-rw-r--r--src/com/google/common/io/protocol/ProtoBufUtil.java19
4 files changed, 171 insertions, 653 deletions
diff --git a/src/com/google/common/io/protocol/IntMap.java b/src/com/google/common/io/protocol/IntMap.java
deleted file mode 100644
index 9f0b8fc..0000000
--- a/src/com/google/common/io/protocol/IntMap.java
+++ /dev/null
@@ -1,383 +0,0 @@
-// Copyright 2009 Google Inc. All Rights Reserved.
-
-package com.google.common.io.protocol;
-
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.NoSuchElementException;
-
-/**
- * A Map from primitive integers to Object values. This stores values
- * for smaller keys in an Object array, and uses {@link Hashtable}
- * only for larger keys. This is specifically designed to be used by
- * J2me protocol buffer runtime ({@link ProtoBuf}, {@link ProtoBufType})
- * to support large tags that are commonly used in Extensions/MessageSet.
- *
- * This class is not thread safe, so the client has to provide
- * appropriate locking mechanism if the map is to be used from
- * multiple threads.
- */
-public class IntMap {
- private static final int MAX_LOWER_BUFFER_SIZE = 64;
- private static final int INITIAL_LOWER_BUFFER_SIZE = 8;
-
- /**
- * An iterator that returns int keys of the IntMap. IntMap has its
- * own Iterator instead of Enumeration to avoid autoboxing. This
- * uses the same buffer of the IntMap, so you should not update the
- * IntMap while the iterator is in use. Once the IntMap is changed,
- * the behavior of preiously obtained iterator is undefined, and a new
- * KeyIterator has to be used instead.
- */
- public class KeyIterator {
- private int oneAheadIndex = 0;
- private int currentKey = Integer.MIN_VALUE;
- private Enumeration higherKeyEnumerator = null;
-
- /**
- * @returns true if there is more keys.
- */
- public boolean hasNext() {
- if (currentKey != Integer.MIN_VALUE) {
- return true;
- }
- if (oneAheadIndex <= maxLowerKey) {
- for (; oneAheadIndex <= maxLowerKey; oneAheadIndex++) {
- if (lower[oneAheadIndex] != null) {
- // record the key, then increment the oneAheadIndex.
- currentKey = oneAheadIndex++;
- return true;
- }
- }
- }
- if (higher != null) {
- if (higherKeyEnumerator == null) {
- higherKeyEnumerator = higher.keys();
- }
- if (higherKeyEnumerator.hasMoreElements()) {
- Integer key = (Integer) higherKeyEnumerator.nextElement();
- currentKey = key.intValue();
- return true;
- }
- }
- return false;
- }
-
- /**
- * @returns next key
- * @throws NoSuchElementException if there is no more keys.
- */
- public int next() {
- if (currentKey == Integer.MIN_VALUE && !hasNext()) {
- throw new NoSuchElementException();
- }
- int key = currentKey;
- currentKey = Integer.MIN_VALUE;
- return key;
- }
- }
-
- /** Stores values for lower keys */
- private Object[] lower;
-
- /** Hashtable for higher tags */
- private Hashtable higher;
-
- /** A maximum key that has been ever added to the lower buffer.*/
- private int maxLowerKey;
-
- /** A maximum key that has been ever added to the map.*/
- private int maxKey;
-
- /** the number of elements in lower buffer */
- private int lowerCount;
-
- /**
- * Constructs an {@link IntMap} with default lower buffer size.
- */
- public IntMap() {
- this(INITIAL_LOWER_BUFFER_SIZE); // can expand 3 times
- }
-
- /**
- * Constructs an {@link IntMap} with the suggested initial lower buffer size.
- * The argument is just a hint and may not be used. If its value is
- * larger than {@link MAX_LOWER_BUFFER_SIZE} or negative, it will use the
- * MAX_LOWER_BUFFER_SIZE instead.
- */
- IntMap(int initialLowerBufferSize) {
- int lowerBufferSize = INITIAL_LOWER_BUFFER_SIZE;
- if (initialLowerBufferSize > 0) {
- lowerBufferSize = Math.min(initialLowerBufferSize, MAX_LOWER_BUFFER_SIZE);
- }
- lower = new Object[lowerBufferSize];
- lowerCount = 0;
- maxKey = Integer.MIN_VALUE;
- maxLowerKey = Integer.MIN_VALUE;
- }
-
- /**
- * A factory method to constructs an {@link IntMap} with the same
- * lower buffer size.
- *
- * @return a new IntMap whose lower buffer size is same as
- * this instance.
- */
- public IntMap newIntMapWithSameBufferSize() {
- return new IntMap(maxLowerKey);
- }
-
- /**
- * @return the {@link KeyIterator} of the map.
- */
- public KeyIterator keys() {
- return new KeyIterator();
- }
-
- /**
- * Returns max key that ever added to the map. Note that this is not
- * max for current state. Removing the max key will not update the
- * max key value. If nothing is added, it will return {@link
- * Integer.MIN_VALUE}, which means you cannot tell if an value is
- * added with MIN_VALUE key.
- */
- public int maxKey() {
- return maxKey;
- }
-
- /**
- * Returns the number of key-value pairs in the map.
- */
- public int size() {
- return higher == null ? lowerCount : lowerCount + higher.size();
- }
-
- /**
- * @return true if the map is empty.
- */
- public boolean isEmpty() {
- return size() == 0;
- }
-
- /**
- * Clears all key/value pairs. The map becomes empty after this
- * operation, but does not release memory.
- */
- public void clear() {
- for (int i = 0; i < lower.length; i++) {
- lower[i] = null;
- }
- if (higher != null) higher.clear();
- maxKey = Integer.MIN_VALUE;
- maxLowerKey = Integer.MIN_VALUE;
- lowerCount = 0;
- }
-
- /**
- * Returns the value associated with the given key.
- *
- * @param key a key
- * @return the value associated with the given key. null if the map has
- * no value for the key.
- */
- public Object get(int key) {
- if (key > maxKey) {
- return null;
- } else if (0 <= key && key <= maxLowerKey) {
- return lower[key];
- } else if (higher != null) {
- return higher.get(key);
- } else {
- return null;
- }
- }
-
- /**
- * Maps the specified key to the given value in the {@link IntMap}.
- * Caveat: Passing null value removes the value from the map. This is to
- * keep the semantics used in {@link ProtoBuf}.
- *
- * @param key a key
- * @param value the value to be added to the Map. If this is null,
- * the key will be removed from the map. This method does nothing if
- * the key does not exist and value is null.
- */
- public void put(int key, Object value) {
- if (value == null) {
- remove(key);
- return;
- }
- expandLowerIfNecessary(key);
- maxKey = Math.max(key, maxKey);
- if (0 <= key && key < lower.length) {
- maxLowerKey = Math.max(key, maxLowerKey);
- if (lower[key] == null) {
- lowerCount++;
- }
- lower[key] = value;
- } else {
- if (higher == null) {
- higher = new Hashtable();
- }
- higher.put(key, value);
- }
- }
-
- /**
- * Removes the key and corresponding value from the {@link IntMap}.
- *
- * @param key the key to remove. This method does nothing if the map does not
- * have the value corresponding for the key.
- * @return the removed value. null if the map does not have the value for
- * the key.
- */
- public Object remove(int key) {
- Object deleted = null;
- if (0 <= key && key < lower.length) {
- deleted = lower[key];
- if (deleted != null) {
- lowerCount--;
- }
- lower[key] = null;
- } else if (higher != null) {
- return higher.remove(key);
- }
- return deleted;
- }
-
- /**
- * Tests if given key has a corresponding value in this {@link IntMap}.
- *
- * @param key possible key.
- * @return <code>true</code> if the given key has the correspoding
- * value. <code>false</code> otherwise.
- */
- public boolean containsKey(int key) {
- if (0 <= key && key < lower.length) {
- return lower[key] != null;
- } else if (higher != null) {
- return higher.containsKey(key);
- }
- return false;
- }
-
- /**
- * Returns hashcode for {@link IntMap}.
- */
- public int hashCode() {
- int hashCode = 1;
- for (int i = 0; i < lower.length ; i++) {
- Object value = lower[i];
- if (value != null) {
- hashCode = 31 * hashCode + value.hashCode() + i;
- }
- }
- // Hashtable in J2me does not implement hashCode(), so we simply
- // use the size of hashtable.
- return higher == null ? hashCode : hashCode + higher.size();
- }
-
- /**
- * Compares the equality. Two IntMaps are considered equals iff
- * both map have the same key-value pairs.
- * Caveat: This assumes that the Class of each value object implements
- * equals correctly. This may not be the case in J2me.
- *
- * @param object an object to be compared with
- * @return true if the specified Object is equal to this IntMap
- */
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null || !(object instanceof IntMap)) {
- return false;
- }
- IntMap peer = (IntMap) object;
- if (size() != peer.size()) {
- return false;
- }
- return compareLowerBuffer(lower, peer.lower) &&
- compareHashtable(higher, peer.higher);
- }
-
- private boolean compareLowerBuffer(Object[] lower1, Object[] lower2) {
- int min = Math.min(lower1.length, lower2.length);
-
- for (int i = 0; i < min; i++) {
- if ((lower1[i] == null && lower2[i] != null) ||
- (lower1[i] != null && !lower1[i].equals(lower2[i]))) {
- return false;
- }
- }
- // make sure there are no values in remaining fields.
- if (lower1.length > lower2.length) {
- for (int i = min; i < lower1.length; i++) {
- if (lower1[i] != null) return false;
- }
- } else if (lower1.length < lower2.length) {
- for (int i = min; i < lower2.length; i++) {
- if (lower2[i] != null) return false;
- }
- }
- return true;
- }
-
- /**
- * J2me's Hashtable does not implement equal, Bummer!
- */
- private static boolean compareHashtable(Hashtable h1, Hashtable h2) {
- if (h1 == h2) { // null == null is caught here
- return true;
- }
- if (h1 == null || h2 == null) {
- return false;
- }
- if (h1.size() != h2.size()) {
- return false;
- }
- // Ensure the values are the same.
- Enumeration h1Keys = h1.keys();
- while (h1Keys.hasMoreElements()) {
- Object key = h1Keys.nextElement();
- Object h1Value = h1.get(key);
- Object h2Value = h2.get(key);
- if (!h1Value.equals(h2Value)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Expands lower buffer iff the key does not fit to current buffer size,
- * but will fit in MAX buffer size.
- */
- private void expandLowerIfNecessary(int key) {
- if (key <= MAX_LOWER_BUFFER_SIZE && key >= lower.length && key > 0) {
- int size = lower.length;
- do {
- size <<= 1;
- } while (size <= key);
- size = Math.min(size, MAX_LOWER_BUFFER_SIZE);
- Object[] newLower = new Object[size];
- System.arraycopy(lower, 0, newLower, 0, lower.length);
- lower = newLower;
- }
- }
-
- /* {@inheritDoc} */
- public String toString() {
- StringBuffer buffer = new StringBuffer("IntMap{lower:");
- for (int i = 0; i < lower.length; i++) {
- if (lower[i] != null) {
- buffer.append(i);
- buffer.append("=>");
- buffer.append(lower[i]);
- buffer.append(", ");
- }
- }
- buffer.append(", higher:" + higher + "}");
- return buffer.toString();
- }
-}
diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java
index ae7e4a6..2a2f8b7 100644
--- a/src/com/google/common/io/protocol/ProtoBuf.java
+++ b/src/com/google/common/io/protocol/ProtoBuf.java
@@ -7,7 +7,9 @@ import java.io.*;
import java.util.*;
/**
- * Protocol buffer message object.
+ * Protocol buffer message object. Currently, it is assumed that tags ids are
+ * not large. This could be improved by storing a start offset, reducing the
+ * assumption to a dense number space.
* <p>
* ProtoBuf instances may or may not reference a ProtoBufType instance,
* representing information from a corresponding .proto file, which defines tag
@@ -31,6 +33,7 @@ import java.util.*;
* this behavior is that default values cannot be removed -- they would reappear
* after a serialization cycle. If a tag has repeated values, setXXX(tag, value)
* will overwrite all of them and getXXX(tag) will throw an exception.
+ *
*/
public class ProtoBuf {
@@ -42,9 +45,7 @@ public class ProtoBuf {
private static final String MSG_MISMATCH = "Type mismatch";
private static final String MSG_UNSUPPORTED = "Unsupp.Type";
- // see
- // http://code.google.com/apis/protocolbuffers/docs/overview.html
- // for more details about wire format.
+ // names copied from //net/proto2/internal/wire_format.cc
static final int WIRETYPE_END_GROUP = 4;
static final int WIRETYPE_FIXED32 = 5;
static final int WIRETYPE_FIXED64 = 1;
@@ -55,19 +56,20 @@ public class ProtoBuf {
/** Maximum number of bytes for VARINT wire format (64 bit, 7 bit/byte) */
private static final int VARINT_MAX_BYTES = 10;
+ private static Long[] SMALL_NUMBERS = {
+ new Long(0), new Long(1), new Long(2), new Long(3), new Long(4),
+ new Long(5), new Long(6), new Long(7), new Long(8), new Long(9),
+ new Long(10), new Long(11), new Long(12), new Long(13), new Long(14),
+ new Long(15)};
+
private ProtoBufType msgType;
- private final IntMap values;
+ private final Vector values = new Vector();
/**
* Wire types picked up on the wire or implied by setters (if no other
* type information is available.
*/
- private final IntMap wireTypes;
-
- /**
- * Saved by a call to #getCachedDataSize(false) and returned in #getCachedSize()
- */
- private int cachedSize = Integer.MIN_VALUE;
+ private final StringBuffer wireTypes = new StringBuffer();
/**
* Creates a protocol message according to the given description. The
@@ -76,22 +78,14 @@ public class ProtoBuf {
*/
public ProtoBuf(ProtoBufType type) {
this.msgType = type;
- if (type != null) {
- // if the type is known, use the type to create IntMaps.
- values = type.newIntMapForProtoBuf();
- wireTypes = type.newIntMapForProtoBuf();
- } else {
- values = new IntMap();
- wireTypes = new IntMap();
- }
}
- /**
+ /**
* Clears all data stored in this ProtoBuf.
*/
public void clear() {
- values.clear();
- wireTypes.clear();
+ values.setSize(0);
+ wireTypes.setLength(0);
}
/**
@@ -216,7 +210,7 @@ public class ProtoBuf {
return (int) ((Long) getObject(tag, ProtoBufType.TYPE_INT32)).longValue();
}
- /**
+ /**
* Returns the integer value for the given repeated tag at the given index.
*/
public int getInt(int tag, int index) {
@@ -311,8 +305,7 @@ public class ProtoBuf {
* @param type the new type
*/
void setType(ProtoBufType type) {
- // reject if the type is already set, or value is alreay set.
- if (!values.isEmpty() ||
+ if (values.size() != 0 ||
(msgType != null && type != null && type != msgType)) {
throw new IllegalArgumentException();
}
@@ -366,8 +359,7 @@ public class ProtoBuf {
* @return this
* @throws IOException raised if an IO exception occurs in the
* underlying stream or the end of the stream is reached at
- * an unexpected position, or if we encounter bad data
- * while reading.
+ * an unexpected position
*/
public int parse(InputStream is, int available) throws IOException {
@@ -384,7 +376,11 @@ public class ProtoBuf {
break;
}
int tag = (int) (tagAndType >>> 3);
- wireTypes.put(tag, wireType);
+ while (wireTypes.length() <= tag){
+ wireTypes.append((char) ProtoBufType.TYPE_UNDEFINED);
+ }
+ wireTypes.setCharAt(tag, (char) wireType);
+
// first step: decode tag value
Object value;
switch (wireType) {
@@ -394,7 +390,8 @@ public class ProtoBuf {
if (isZigZagEncodedType(tag)) {
v = zigZagDecode(v);
}
- value = v;
+ value = (v >= 0 && v < SMALL_NUMBERS.length) ?
+ SMALL_NUMBERS[(int) v] : new Long(v);
break;
// also used for fixed values
@@ -411,7 +408,9 @@ public class ProtoBuf {
shift += 8;
}
- value = v;
+ value = (v >= 0 && v < SMALL_NUMBERS.length)
+ ? SMALL_NUMBERS[(int) v]
+ : new Long(v);
break;
case WIRETYPE_LENGTH_DELIMITED:
@@ -446,8 +445,7 @@ public class ProtoBuf {
break;
default:
- throw new IOException("Unknown wire type " + wireType +
- ", reading garbage data?");
+ throw new RuntimeException(MSG_UNSUPPORTED + wireType);
}
insertObject(tag, getCount(tag), value);
}
@@ -468,9 +466,9 @@ public class ProtoBuf {
throw new ArrayIndexOutOfBoundsException();
}
if (count == 1){
- values.remove(tag);
+ values.setElementAt(null, tag);
} else {
- Vector v = (Vector) values.get(tag);
+ Vector v = (Vector) values.elementAt(tag);
v.removeElementAt(index);
}
}
@@ -479,15 +477,12 @@ public class ProtoBuf {
* Returns the number of repeated and optional (0..1) values for a given tag.
* Note: Default values are not counted (and in general not considered in
* access methods for repeated tags), but considered for has(tag).
- *
- * @param tag the tag of the field
- * @throws ArrayIndexOutOfBoundsException when tag is < 0
*/
public int getCount(int tag) {
- if (tag < 0) {
- throw new ArrayIndexOutOfBoundsException(tag);
+ if (tag >= values.size()){
+ return 0;
}
- Object o = values.get(tag);
+ Object o = values.elementAt(tag);
if (o == null){
return 0;
}
@@ -507,69 +502,38 @@ public class ProtoBuf {
tagType = msgType.getType(tag);
}
- if (tagType == ProtoBufType.TYPE_UNDEFINED) {
- Integer tagTypeObj = (Integer) wireTypes.get(tag);
- if (tagTypeObj != null) {
- tagType = tagTypeObj.intValue();
- }
+ if (tagType == ProtoBufType.TYPE_UNDEFINED && tag < wireTypes.length()) {
+ tagType = wireTypes.charAt(tag);
}
-
+
if (tagType == ProtoBufType.TYPE_UNDEFINED && getCount(tag) > 0) {
Object o = getObject(tag, 0, ProtoBufType.TYPE_UNDEFINED);
-
- tagType = (o instanceof Long) || (o instanceof Boolean)
+
+ tagType = (o instanceof Long) || (o instanceof Boolean)
? WIRETYPE_VARINT : WIRETYPE_LENGTH_DELIMITED;
}
-
+
return tagType;
}
-
+
/**
* Returns the number of bytes needed to store this protocol buffer
*/
public int getDataSize() {
- return getCachedDataSize(false /* don't trust cache */);
- }
-
- /**
- * Each Protobuf keeps track of a <code> cachedSize </code> that is
- * used to short circuit evaluation of its children's sizes. This value
- * should only be trusted if you are reasonably certain it cannot be
- * corrupt. (A corrupt cache can happen if any child ProtoBuf of this
- * ProtoBuf has had a value set. In general, it is best to only trust
- * the cache if you have just finished cleansing it.)
- *
- * <P/>The cache can be cleansed by calling this method with
- * trustCache = false.
- *
- * @param trustCache if the cached size should be trusted. Set false to
- * recompuate the size.
- */
- private int getCachedDataSize(boolean trustCache) {
- if (cachedSize != Integer.MIN_VALUE && trustCache) {
- return cachedSize;
- }
int size = 0;
- IntMap.KeyIterator itr = values.keys();
- while(itr.hasNext()) {
- int tag = itr.next();
+ for (int tag = 0; tag <= maxTag(); tag++) {
for (int i = 0; i < getCount(tag); i++) {
- size += getCachedDataSize(tag, i, trustCache);
+ size += getDataSize(tag, i);
}
- }
- cachedSize = size;
-
- return cachedSize;
+ }
+ return size;
}
-
- /**
- * Returns the size of the child.
- *
- * @param tag tag used to determine the type of this child
- * @param i used to determine which count this child is
- * @param trustSizeCache passed down to #getCachedDataSize()
+
+
+ /**
+ * Returns the size of the given value
*/
- private int getCachedDataSize(int tag, int i, boolean trustSizeCache) {
+ private int getDataSize(int tag, int i) {
int tagSize = getVarIntSize(tag << 3);
switch(getWireType(tag)){
@@ -587,20 +551,20 @@ public class ProtoBuf {
// take end group into account....
return tagSize + getProtoBuf(tag, i).getDataSize() + tagSize;
}
-
+
// take the object as stored
Object o = getObject(tag, i, ProtoBufType.TYPE_UNDEFINED);
-
+
int contentSize;
-
- if (o instanceof byte[]) {
+
+ if (o instanceof byte[]){
contentSize = ((byte[]) o).length;
} else if (o instanceof String) {
contentSize = encodeUtf8((String) o, null, 0);
} else {
- contentSize = ((ProtoBuf) o).getCachedDataSize(trustSizeCache);
+ contentSize = ((ProtoBuf) o).getDataSize();
}
-
+
return tagSize + getVarIntSize(contentSize) + contentSize;
}
@@ -620,93 +584,67 @@ public class ProtoBuf {
return size;
}
- /**
+ /**
* Writes this and nested protocol buffers to the given output stream.
*
* @param os target output stream
- * @throws IOException thrown if there is an IOException
+ * @throws IOException thrown if there is an IOException
*/
public void outputTo(OutputStream os) throws IOException {
- // We can't know what changed since we last output, so refresh the children.
- getDataSize();
- outputToInternal(os);
- }
-
- /**
- * Recursive output method wrapped by #outputTo()
- *
- * @param os target output stream
- * @throws IOException thrown if there is an IOException
- */
- private void outputToInternal(OutputStream os) throws IOException {
- IntMap.KeyIterator itr = values.keys();
- while (itr.hasNext()) {
- int tag = itr.next();
- outputField(tag, os);
- }
- }
-
- /**
- * Output a field indicated by the tag to given stream.
- *
- * @param tag the tag of the field to output.
- * @param os target output stream
- * @throws IOException thrown if there is an IOException
- */
- private void outputField(int tag, OutputStream os) throws IOException {
- int size = getCount(tag);
- int wireType = getWireType(tag);
- int wireTypeTag = (tag << 3) | wireType;
-
- // ignore default values
- for (int i = 0; i < size; i++) {
- writeVarInt(os, wireTypeTag);
- switch (wireType) {
- case WIRETYPE_FIXED32:
- case WIRETYPE_FIXED64:
- long v = ((Long) getObject(tag, i, ProtoBufType.TYPE_INT64))
- .longValue();
- int cnt = (wireType == WIRETYPE_FIXED32) ? 4 : 8;
- for (int b = 0; b < cnt; b++) {
- os.write((int) (v & 0x0ff));
- v >>= 8;
- }
- break;
-
- case WIRETYPE_VARINT:
- v = ((Long) getObject(tag, i, ProtoBufType.TYPE_INT64)).longValue();
- if (isZigZagEncodedType(tag)) {
- v = zigZagEncode(v);
- }
- writeVarInt(os, v);
- break;
-
- case WIRETYPE_LENGTH_DELIMITED:
- Object o = getObject(tag, i,
- getType(tag) == ProtoBufType.TYPE_MESSAGE
- ? ProtoBufType.TYPE_UNDEFINED
- : ProtoBufType.TYPE_DATA);
-
- if (o instanceof byte[]) {
- byte[] data = (byte[]) o;
- writeVarInt(os, data.length);
- os.write(data);
- } else {
-
- ProtoBuf msg = (ProtoBuf) o;
- writeVarInt(os, msg.getCachedDataSize(true));
- msg.outputToInternal(os);
- }
- break;
-
- case WIRETYPE_START_GROUP:
- ((ProtoBuf) getObject(tag, i, ProtoBufType.TYPE_GROUP))
- .outputToInternal(os);
- writeVarInt(os, (tag << 3) | WIRETYPE_END_GROUP);
- break;
+ for (int tag = 0; tag <= maxTag(); tag++) {
+ int size = getCount(tag);
+ int wireType = getWireType(tag);
+
+ // ignore default values
+ for (int i = 0; i < size; i++) {
+ writeVarInt(os, (tag << 3) | wireType);
+
+ switch (wireType) {
+ case WIRETYPE_FIXED32:
+ case WIRETYPE_FIXED64:
+ long v = ((Long) getObject(tag, i, ProtoBufType.TYPE_INT64))
+ .longValue();
+ int cnt = (wireType == WIRETYPE_FIXED32) ? 4 : 8;
+ for (int b = 0; b < cnt; b++) {
+ os.write((int) (v & 0x0ff));
+ v >>= 8;
+ }
+ break;
- default:
- throw new IllegalArgumentException();
+ case WIRETYPE_VARINT:
+ v = ((Long) getObject(tag, i, ProtoBufType.TYPE_INT64)).longValue();
+ if (isZigZagEncodedType(tag)) {
+ v = zigZagEncode(v);
+ }
+ writeVarInt(os, v);
+ break;
+
+ case WIRETYPE_LENGTH_DELIMITED:
+ Object o = getObject(tag, i,
+ getType(tag) == ProtoBufType.TYPE_MESSAGE
+ ? ProtoBufType.TYPE_UNDEFINED
+ : ProtoBufType.TYPE_DATA);
+
+ if (o instanceof byte[]){
+ byte[] data = (byte[]) o;
+ writeVarInt(os, data.length);
+ os.write(data);
+ } else {
+ ProtoBuf msg = (ProtoBuf) o;
+ writeVarInt(os, msg.getDataSize());
+ msg.outputTo(os);
+ }
+ break;
+
+ case WIRETYPE_START_GROUP:
+ ((ProtoBuf) getObject(tag, i, ProtoBufType.TYPE_GROUP))
+ .outputTo(os);
+ writeVarInt(os, (tag << 3) | WIRETYPE_END_GROUP);
+ break;
+
+ default:
+ throw new IllegalArgumentException();
+ }
}
}
}
@@ -755,12 +693,12 @@ public class ProtoBuf {
outputTo(baos);
return baos.toByteArray();
}
-
+
/**
* Returns the largest tag id used in this message (to simplify testing).
*/
public int maxTag() {
- return values.maxKey();
+ return values.size() - 1;
}
/**
@@ -788,7 +726,8 @@ public class ProtoBuf {
* Sets the given tag to the given long value.
*/
public void setLong(int tag, long value) {
- setObject(tag, value);
+ setObject(tag, value >= 0 && value < SMALL_NUMBERS.length
+ ? SMALL_NUMBERS[(int) value] : new Long(value));
}
/**
@@ -856,7 +795,8 @@ public class ProtoBuf {
* Inserts the given long value for the given tag at the given index.
*/
public void insertLong(int tag, int index, long value) {
- insertObject(tag, index, value);
+ insertObject(tag, index, value >= 0 && value < SMALL_NUMBERS.length
+ ? SMALL_NUMBERS[(int) value] : new Long(value));
}
/**
@@ -939,8 +879,8 @@ public class ProtoBuf {
case ProtoBufType.TYPE_GROUP:
case ProtoBufType.TYPE_MESSAGE:
if (msgType == null || msgType.getData(tag) == null ||
- ((ProtoBuf) object).msgType == null ||
- ((ProtoBuf) object).msgType.equals(msgType.getData(tag))) {
+ ((ProtoBuf) object).msgType == null ||
+ ((ProtoBuf) object).msgType == msgType.getData(tag)) {
return;
}
}
@@ -1004,7 +944,7 @@ public class ProtoBuf {
throw new ArrayIndexOutOfBoundsException();
}
- Object o = values.get(tag);
+ Object o = values.elementAt(tag);
Vector v = null;
if (o instanceof Vector) {
@@ -1085,14 +1025,14 @@ public class ProtoBuf {
if (count == 0) {
setObject(tag, o);
} else {
- Object curr = values.get(tag);
+ Object curr = values.elementAt(tag);
Vector v;
if (curr instanceof Vector) {
v = (Vector) curr;
} else {
v = new Vector();
v.addElement(curr);
- values.put(tag, v);
+ values.setElementAt(v, tag);
}
v.insertElementAt(o, index);
}
@@ -1102,7 +1042,7 @@ public class ProtoBuf {
* Converts the object if a better suited class exists for the given .proto
* type. If the formats are not compatible, an exception is thrown.
*/
- private static Object convert(Object obj, int tagType) {
+ private Object convert(Object obj, int tagType) {
switch (tagType) {
case ProtoBufType.TYPE_UNDEFINED:
return obj;
@@ -1128,7 +1068,7 @@ public class ProtoBuf {
case ProtoBufType.TYPE_SINT32:
case ProtoBufType.TYPE_SINT64:
if (obj instanceof Boolean) {
- return ((Boolean) obj).booleanValue() ? 1 : 0;
+ return SMALL_NUMBERS[((Boolean) obj).booleanValue() ? 1 : 0];
}
return obj;
case ProtoBufType.TYPE_DATA:
@@ -1213,13 +1153,13 @@ public class ProtoBuf {
* values.
*/
private void setObject(int tag, Object o) {
- if (tag < 0) {
- throw new ArrayIndexOutOfBoundsException();
+ if (values.size() <= tag) {
+ values.setSize(tag + 1);
}
if (o != null) {
assertTypeMatch(tag, o);
}
- values.put(tag, o);
+ values.setElementAt(o, tag);
}
/**
diff --git a/src/com/google/common/io/protocol/ProtoBufType.java b/src/com/google/common/io/protocol/ProtoBufType.java
index 728346f..4b6408e 100644
--- a/src/com/google/common/io/protocol/ProtoBufType.java
+++ b/src/com/google/common/io/protocol/ProtoBufType.java
@@ -6,8 +6,9 @@ package com.google.common.io.protocol;
import java.util.*;
/**
- * This class can be used to create a memory model of a .proto file.
- *
+ * This class can be used to create a memory model of a .proto file. Currently,
+ * it is assumed that tags ids are not large. This could be improved by storing
+ * a start offset, relaxing the assumption to a dense number space.
*/
public class ProtoBufType {
// Note: Values 0..15 are reserved for wire types!
@@ -41,46 +42,11 @@ public class ProtoBufType {
public static final int REQUIRED = 0x100;
public static final int OPTIONAL = 0x200;
public static final int REPEATED = 0x400;
-
- private final IntMap types = new IntMap();
-
- /*
- * A struct to store field type and default object.
- * Two TypeInfo objects are equal iff both have the
- * euqal type and object.
- */
- static class TypeInfo {
- private int type;
- private Object data;
- TypeInfo(int t, Object d) {
- type = t;
- data = d;
- }
-
- public int hashCode() {
- return type;
- }
-
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || !(obj instanceof TypeInfo)) {
- return false;
- }
- TypeInfo peerTypeInfo = (TypeInfo) obj;
- return type == peerTypeInfo.type &&
- (data == peerTypeInfo.data ||
- (data != null && data.equals(peerTypeInfo.data)));
- }
-
- public String toString() {
- return "TypeInfo{type=" + type + ", data=" + data + "}";
- }
- };
-
+
+ private final StringBuffer types = new StringBuffer();
+ private final Vector data = new Vector();
private final String typeName;
-
+
/**
* Empty constructor.
*/
@@ -108,36 +74,35 @@ public class ProtoBufType {
* @return this is returned to permit cascading
*/
public ProtoBufType addElement(int optionsAndType, int tag, Object data) {
- types.put(tag, new TypeInfo(optionsAndType, data));
- return this;
- }
+ while (types.length() <= tag) {
+ types.append((char) TYPE_UNDEFINED);
+ this.data.addElement(null);
+ }
+ types.setCharAt(tag, (char) optionsAndType);
+ this.data.setElementAt(data, tag);
- /**
- * Returns a IntMap that has the same lower buffer size as types.
- * This is for ProtoBuf to create IntMap with pre-allocated
- * internal buffer.
- */
- /* package protected */ IntMap newIntMapForProtoBuf() {
- return types.newIntMapWithSameBufferSize();
+ return this;
}
-
+
/**
* Returns the type for the given tag id (without modifiers such as OPTIONAL,
* REPEATED). For undefined tags, TYPE_UNDEFINED is returned.
*/
public int getType(int tag) {
- TypeInfo typeInfo = (TypeInfo) types.get(tag);
- return typeInfo == null ? TYPE_UNDEFINED : typeInfo.type & MASK_TYPE;
+ return (tag < 0 || tag >= types.length())
+ ? TYPE_UNDEFINED
+ : (types.charAt(tag) & MASK_TYPE);
}
- /**
+ /**
* Returns a bit combination of the modifiers for the given tag id
* (OPTIONAL, REPEATED, REQUIRED). For undefined tags, OPTIONAL|REPEATED
* is returned.
- */
+ */
public int getModifiers(int tag) {
- TypeInfo typeInfo = (TypeInfo) types.get(tag);
- return typeInfo == null ? (OPTIONAL | REPEATED) : typeInfo.type & MASK_MODIFIER;
+ return (tag < 0 || tag >= types.length())
+ ? (OPTIONAL | REPEATED)
+ : (types.charAt(tag) & MASK_MODIFIER);
}
/**
@@ -146,15 +111,14 @@ public class ProtoBufType {
* tags, null is returned.
*/
public Object getData(int tag) {
- TypeInfo typeInfo = (TypeInfo) types.get(tag);
- return typeInfo == null ? typeInfo : typeInfo.data;
+ return (tag < 0 || tag >= data.size()) ? null : data.elementAt(tag);
}
/**
* Returns the type name set in the constructor for debugging purposes.
*/
public String toString() {
- return "ProtoBufType Name: " + typeName;
+ return typeName;
}
/**
@@ -174,9 +138,9 @@ public class ProtoBufType {
}
ProtoBufType other = (ProtoBufType) object;
- return types.equals(other.types);
+ return stringEquals(types, other.types);
}
-
+
/**
* {@inheritDoc}
*/
@@ -187,4 +151,20 @@ public class ProtoBufType {
return super.hashCode();
}
}
+
+ public static boolean stringEquals(CharSequence a, CharSequence b) {
+ if (a == b) return true;
+ int length;
+ if (a != null && b != null && (length = a.length()) == b.length()) {
+ if (a instanceof String && b instanceof String) {
+ return a.equals(b);
+ } else {
+ for (int i = 0; i < length; i++) {
+ if (a.charAt(i) != b.charAt(i)) return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/google/common/io/protocol/ProtoBufUtil.java b/src/com/google/common/io/protocol/ProtoBufUtil.java
index b47e79c..72e1bca 100644
--- a/src/com/google/common/io/protocol/ProtoBufUtil.java
+++ b/src/com/google/common/io/protocol/ProtoBufUtil.java
@@ -22,25 +22,6 @@ public final class ProtoBufUtil {
}
}
- /** Convenience method to return a string value from of a proto or null. */
- public static String getProtoValueOrNull(ProtoBuf proto, int tag) {
- try {
- return (proto != null && proto.has(tag)) ? proto.getString(tag) : null;
- } catch (ClassCastException e) {
- return null;
- }
- }
-
- /** Convenience method to return a string value from of a proto or null. */
- public static String getProtoValueOrNull(ProtoBuf proto, int tag, int index) {
- try {
- return (proto != null && proto.has(tag) && proto.getCount(tag) > index) ?
- proto.getString(tag, index) : null;
- } catch (ClassCastException e) {
- return null;
- }
- }
-
/** Convenience method to return a string value from of a sub-proto or "". */
public static String getSubProtoValueOrEmpty(
ProtoBuf proto, int sub, int tag) {