|
|
@@ -32,8 +32,8 @@ import java.util.*;
|
|
|
* but only the simple methods take default values into account. The reason for
|
|
|
* 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.
|
|
|
- *
|
|
|
+ * will overwrite all of them and getXXX(tag) will throw an exception.
|
|
|
+ *
|
|
|
*/
|
|
|
|
|
|
public class ProtoBuf {
|
|
|
@@ -46,12 +46,12 @@ public class ProtoBuf {
|
|
|
private static final String MSG_UNSUPPORTED = "Unsupp.Type";
|
|
|
|
|
|
// names copied from //net/proto2/internal/wire_format.cc
|
|
|
- private static final int WIRETYPE_END_GROUP = 4;
|
|
|
- private static final int WIRETYPE_FIXED32 = 5;
|
|
|
- private static final int WIRETYPE_FIXED64 = 1;
|
|
|
- private static final int WIRETYPE_LENGTH_DELIMITED = 2;
|
|
|
- private static final int WIRETYPE_START_GROUP = 3;
|
|
|
- private static final int WIRETYPE_VARINT = 0;
|
|
|
+ static final int WIRETYPE_END_GROUP = 4;
|
|
|
+ static final int WIRETYPE_FIXED32 = 5;
|
|
|
+ static final int WIRETYPE_FIXED64 = 1;
|
|
|
+ static final int WIRETYPE_LENGTH_DELIMITED = 2;
|
|
|
+ static final int WIRETYPE_START_GROUP = 3;
|
|
|
+ static final int WIRETYPE_VARINT = 0;
|
|
|
|
|
|
/** Maximum number of bytes for VARINT wire format (64 bit, 7 bit/byte) */
|
|
|
private static final int VARINT_MAX_BYTES = 10;
|
|
|
@@ -62,7 +62,7 @@ public class ProtoBuf {
|
|
|
new Long(10), new Long(11), new Long(12), new Long(13), new Long(14),
|
|
|
new Long(15)};
|
|
|
|
|
|
- private final ProtoBufType msgType;
|
|
|
+ private ProtoBufType msgType;
|
|
|
private final Vector values = new Vector();
|
|
|
|
|
|
/**
|
|
|
@@ -123,6 +123,20 @@ public class ProtoBuf {
|
|
|
insertLong(tag, getCount(tag), value);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Appends the given (repeated) tag with the given float value.
|
|
|
+ */
|
|
|
+ public void addFloat(int tag, float value) {
|
|
|
+ insertFloat(tag, getCount(tag), value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Appends the given (repeated) tag with the given double value.
|
|
|
+ */
|
|
|
+ public void addDouble(int tag, double value) {
|
|
|
+ insertDouble(tag, getCount(tag), value);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Appends the given (repeated) tag with the given group or message value.
|
|
|
*/
|
|
|
@@ -130,6 +144,28 @@ public class ProtoBuf {
|
|
|
insertProtoBuf(tag, getCount(tag), value);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Adds a new protobuf for the specified tag, setting the child protobuf's
|
|
|
+ * type correctly for the tag.
|
|
|
+ * @param tag the tag for which to create a new protobuf
|
|
|
+ * @return the newly created protobuf
|
|
|
+ */
|
|
|
+ public ProtoBuf addNewProtoBuf(int tag) {
|
|
|
+ ProtoBuf child = newProtoBufForTag(tag);
|
|
|
+ addProtoBuf(tag, child);
|
|
|
+ return child;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates and returns a new protobuf for the specified tag, setting the new
|
|
|
+ * protobuf's type correctly for the tag.
|
|
|
+ * @param tag the tag for which to create a new protobuf
|
|
|
+ * @return the newly created protobuf
|
|
|
+ */
|
|
|
+ public ProtoBuf newProtoBufForTag(int tag) {
|
|
|
+ return new ProtoBuf((ProtoBufType) msgType.getData(tag));
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Appends the given (repeated) tag with the given String value.
|
|
|
*/
|
|
|
@@ -167,7 +203,7 @@ public class ProtoBuf {
|
|
|
return (byte[]) getObject(tag, index, ProtoBufType.TYPE_DATA);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
+ /**
|
|
|
* Returns the integer value for the given tag.
|
|
|
*/
|
|
|
public int getInt(int tag) {
|
|
|
@@ -182,7 +218,7 @@ public class ProtoBuf {
|
|
|
ProtoBufType.TYPE_INT32)).longValue();
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
+ /**
|
|
|
* Returns the long value for the given tag.
|
|
|
*/
|
|
|
public long getLong(int tag) {
|
|
|
@@ -196,7 +232,35 @@ public class ProtoBuf {
|
|
|
return ((Long) getObject(tag, index, ProtoBufType.TYPE_INT64)).longValue();
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
+ /**
|
|
|
+ * Returns the float value for the given tag.
|
|
|
+ */
|
|
|
+ public float getFloat(int tag) {
|
|
|
+ return Float.intBitsToFloat(getInt(tag));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the float value for the given repeated tag at the given index.
|
|
|
+ */
|
|
|
+ public float getFloat(int tag, int index) {
|
|
|
+ return Float.intBitsToFloat(getInt(tag, index));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the double value for the given tag.
|
|
|
+ */
|
|
|
+ public double getDouble(int tag) {
|
|
|
+ return Double.longBitsToDouble(getLong(tag));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the double value for the given repeated tag at the given index.
|
|
|
+ */
|
|
|
+ public double getDouble(int tag, int index) {
|
|
|
+ return Double.longBitsToDouble(getLong(tag, index));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* Returns the group or nested message for the given tag.
|
|
|
*/
|
|
|
public ProtoBuf getProtoBuf(int tag) {
|
|
|
@@ -227,13 +291,27 @@ public class ProtoBuf {
|
|
|
return (String) getObject(tag, index, ProtoBufType.TYPE_TEXT);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
+ /**
|
|
|
* Returns the type definition of this protocol buffer or group -- if set.
|
|
|
*/
|
|
|
public ProtoBufType getType() {
|
|
|
return msgType;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the type definition of this protocol buffer. Used internally in
|
|
|
+ * ProtoBufUtil for incremental reading.
|
|
|
+ *
|
|
|
+ * @param type the new type
|
|
|
+ */
|
|
|
+ void setType(ProtoBufType type) {
|
|
|
+ if (values.size() != 0 ||
|
|
|
+ (msgType != null && type != null && type != msgType)) {
|
|
|
+ throw new IllegalArgumentException();
|
|
|
+ }
|
|
|
+ this.msgType = type;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Convenience method for determining whether a tag has a value. Note: in
|
|
|
* contrast to getCount(tag) > 0, this method takes the default value
|
|
|
@@ -652,6 +730,20 @@ public class ProtoBuf {
|
|
|
? SMALL_NUMBERS[(int) value] : new Long(value));
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the given tag to the given double value.
|
|
|
+ */
|
|
|
+ public void setDouble(int tag, double value) {
|
|
|
+ setLong(tag, Double.doubleToLongBits(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets the given tag to the given float value.
|
|
|
+ */
|
|
|
+ public void setFloat(int tag, float value) {
|
|
|
+ setInt(tag, Float.floatToIntBits(value));
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Sets the given tag to the given Group or nested Message.
|
|
|
*/
|
|
|
@@ -659,6 +751,18 @@ public class ProtoBuf {
|
|
|
setObject(tag, pb);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets a new protobuf for the specified tag, setting the child protobuf's
|
|
|
+ * type correctly for the tag.
|
|
|
+ * @param tag the tag for which to create a new protobuf
|
|
|
+ * @return the newly created protobuf
|
|
|
+ */
|
|
|
+ public ProtoBuf setNewProtoBuf(int tag) {
|
|
|
+ ProtoBuf child = newProtoBufForTag(tag);
|
|
|
+ setProtoBuf(tag, child);
|
|
|
+ return child;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Sets the given tag to the given String value.
|
|
|
*/
|
|
|
@@ -695,6 +799,20 @@ public class ProtoBuf {
|
|
|
? SMALL_NUMBERS[(int) value] : new Long(value));
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Inserts the given float value for the given tag at the given index.
|
|
|
+ */
|
|
|
+ public void insertFloat(int tag, int index, float value) {
|
|
|
+ insertInt(tag, index, Float.floatToIntBits(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Inserts the given double value for the given tag at the given index.
|
|
|
+ */
|
|
|
+ public void insertDouble(int tag, int index, double value) {
|
|
|
+ insertLong(tag, index, Double.doubleToLongBits(value));
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Inserts the given group or message for the given tag at the given index.
|
|
|
*/
|
|
|
@@ -739,6 +857,8 @@ public class ProtoBuf {
|
|
|
case ProtoBufType.TYPE_UINT64:
|
|
|
case ProtoBufType.TYPE_SINT32:
|
|
|
case ProtoBufType.TYPE_SINT64:
|
|
|
+ case ProtoBufType.TYPE_FLOAT:
|
|
|
+ case ProtoBufType.TYPE_DOUBLE:
|
|
|
return;
|
|
|
}
|
|
|
} else if (object instanceof byte[]){
|
|
|
@@ -1078,21 +1198,21 @@ public class ProtoBuf {
|
|
|
/**
|
|
|
* Encodes the given string to UTF-8 in the given buffer or calculates
|
|
|
* the space needed if the buffer is null.
|
|
|
- *
|
|
|
+ *
|
|
|
* @param s the string to be UTF-8 encoded
|
|
|
* @param buf byte array to write to
|
|
|
- * @return new buffer position after writing (which equals the required size
|
|
|
+ * @return new buffer position after writing (which equals the required size
|
|
|
* if pos is 0)
|
|
|
*/
|
|
|
static int encodeUtf8(String s, byte[] buf, int pos){
|
|
|
int len = s.length();
|
|
|
for (int i = 0; i < len; i++){
|
|
|
int code = s.charAt(i);
|
|
|
-
|
|
|
+
|
|
|
// surrogate 0xd800 .. 0xdfff?
|
|
|
if (code >= 0x0d800 && code <= 0x0dfff && i + 1 < len){
|
|
|
int codeLo = s.charAt(i + 1);
|
|
|
-
|
|
|
+
|
|
|
// 0xfc00 is the surrogate id mask (first six bit of 16 set)
|
|
|
// 0x03ff is the surrogate data mask (remaining 10 bit)
|
|
|
// check if actually a surrogate pair (d800 ^ dc00 == 0400)
|
|
|
@@ -1137,35 +1257,35 @@ public class ProtoBuf {
|
|
|
buf[pos + 2] = (byte) ((0x80 | ((code >> 6) & 0x3F)));
|
|
|
buf[pos + 3] = (byte) ((0x80 | (code & 0x3F)));
|
|
|
}
|
|
|
- pos += 4;
|
|
|
+ pos += 4;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return pos;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Decodes an array of UTF-8 bytes to a Java string (UTF-16). The tolerant
|
|
|
- * flag determines what to do in case of illegal or unsupported sequences.
|
|
|
- *
|
|
|
- * @param data input byte array containing UTF-8 data
|
|
|
+ * Decodes an array of UTF-8 bytes to a Java string (UTF-16). The tolerant
|
|
|
+ * flag determines what to do in case of illegal or unsupported sequences.
|
|
|
+ *
|
|
|
+ * @param data input byte array containing UTF-8 data
|
|
|
* @param start decoding start position in byte array
|
|
|
* @param end decoding end position in byte array
|
|
|
- * @param tolerant if true, an IllegalArgumentException is thrown for illegal
|
|
|
+ * @param tolerant if true, an IllegalArgumentException is thrown for illegal
|
|
|
* UTF-8 codes
|
|
|
* @return the string containing the UTF-8 decoding result
|
|
|
*/
|
|
|
- static String decodeUtf8(byte[] data, int start, int end,
|
|
|
+ static String decodeUtf8(byte[] data, int start, int end,
|
|
|
boolean tolerant){
|
|
|
-
|
|
|
+
|
|
|
StringBuffer sb = new StringBuffer(end - start);
|
|
|
int pos = start;
|
|
|
-
|
|
|
+
|
|
|
while (pos < end){
|
|
|
int b = data[pos++] & 0x0ff;
|
|
|
if (b <= 0x7f){
|
|
|
sb.append((char) b);
|
|
|
- } else if (b >= 0xf5){ // byte sequence too long
|
|
|
+ } else if (b >= 0xf5){ // byte sequence too long
|
|
|
if (!tolerant){
|
|
|
throw new IllegalArgumentException("Invalid UTF8");
|
|
|
}
|
|
|
@@ -1173,16 +1293,16 @@ public class ProtoBuf {
|
|
|
} else {
|
|
|
int border = 0xe0;
|
|
|
int count = 1;
|
|
|
- int minCode = 128;
|
|
|
+ int minCode = 128;
|
|
|
int mask = 0x01f;
|
|
|
while (b >= border){
|
|
|
border = (border >> 1) | 0x80;
|
|
|
- minCode = minCode << (count == 1 ? 4 : 5);
|
|
|
+ minCode = minCode << (count == 1 ? 4 : 5);
|
|
|
count++;
|
|
|
mask = mask >> 1;
|
|
|
}
|
|
|
int code = b & mask;
|
|
|
-
|
|
|
+
|
|
|
for (int i = 0; i < count; i++){
|
|
|
code = code << 6;
|
|
|
if (pos >= end){
|
|
|
@@ -1197,7 +1317,7 @@ public class ProtoBuf {
|
|
|
code |= (data[pos++] & 0x3f); // six bit
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// illegal code or surrogate code
|
|
|
if (!tolerant && code < minCode || (code >= 0xd800 && code <= 0xdfff)){
|
|
|
throw new IllegalArgumentException("Invalid UTF8");
|