From f83a870b187ed66a9a14e3ae8e27b2af922d85df Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 10 Feb 2009 15:43:59 -0800 Subject: [PATCH 1/6] auto import from //branches/cupcake/...@130745 --- src/com/google/common/io/protocol/ProtoBuf.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java index 679a4da1e5..f2af32fae7 100644 --- a/src/com/google/common/io/protocol/ProtoBuf.java +++ b/src/com/google/common/io/protocol/ProtoBuf.java @@ -748,6 +748,7 @@ public class ProtoBuf { case ProtoBufType.TYPE_MESSAGE: case ProtoBufType.TYPE_TEXT: case ProtoBufType.TYPE_BYTES: + case ProtoBufType.TYPE_STRING: return; } } else if (object instanceof ProtoBuf) { From 52067f67ad358ac16ccc8f8ecbd5a5dc6c4d661a Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Thu, 19 Feb 2009 10:57:31 -0800 Subject: [PATCH 2/6] auto import from //branches/cupcake/...@132276 --- .../google/common/io/protocol/ProtoBuf.java | 143 ++++++++++++---- .../common/io/protocol/ProtoBufType.java | 50 +++++- .../common/io/protocol/ProtoBufUtil.java | 160 ++++++++++++++++-- 3 files changed, 312 insertions(+), 41 deletions(-) diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java index f2af32fae7..8ebd1b9a33 100644 --- a/src/com/google/common/io/protocol/ProtoBuf.java +++ b/src/com/google/common/io/protocol/ProtoBuf.java @@ -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. */ @@ -196,6 +210,34 @@ 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. */ @@ -234,6 +276,20 @@ public class ProtoBuf { 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 +708,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. */ @@ -695,6 +765,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 +823,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[]){ @@ -748,7 +834,6 @@ public class ProtoBuf { case ProtoBufType.TYPE_MESSAGE: case ProtoBufType.TYPE_TEXT: case ProtoBufType.TYPE_BYTES: - case ProtoBufType.TYPE_STRING: return; } } else if (object instanceof ProtoBuf) { @@ -1079,21 +1164,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) @@ -1138,35 +1223,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"); } @@ -1174,16 +1259,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){ @@ -1198,7 +1283,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"); diff --git a/src/com/google/common/io/protocol/ProtoBufType.java b/src/com/google/common/io/protocol/ProtoBufType.java index 1aec8f9101..4b6408e888 100644 --- a/src/com/google/common/io/protocol/ProtoBufType.java +++ b/src/com/google/common/io/protocol/ProtoBufType.java @@ -1,4 +1,4 @@ -// Copyright 2007 The Android Open Source Project +// Copyright 2007 Google Inc. // All Rights Reserved. package com.google.common.io.protocol; @@ -9,7 +9,6 @@ import java.util.*; * 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! @@ -121,4 +120,51 @@ public class ProtoBufType { public String toString() { return typeName; } + + /** + * {@inheritDoc} + *

Two ProtoBufTypes are equals if the fields types are the same. + */ + public boolean equals(Object object) { + if (null == object) { + // trivial check + return false; + } else if (this == object) { + // trivial check + return true; + } else if (this.getClass() != object.getClass()) { + // different class + return false; + } + ProtoBufType other = (ProtoBufType) object; + + return stringEquals(types, other.types); + } + + /** + * {@inheritDoc} + */ + public int hashCode() { + if (types != null) { + return types.hashCode(); + } else { + 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 077bd3d34b..72e1bca9ed 100644 --- a/src/com/google/common/io/protocol/ProtoBufUtil.java +++ b/src/com/google/common/io/protocol/ProtoBufUtil.java @@ -1,7 +1,9 @@ -// Copyright 2008 The Android Open Source Project +// Copyright 2008 Google Inc. All Rights Reserved. package com.google.common.io.protocol; +import java.io.*; + /** * Utility functions for dealing with ProtoBuf objects consolidated from * previous spot implementations across the codebase. @@ -24,14 +26,39 @@ public final class ProtoBufUtil { public static String getSubProtoValueOrEmpty( ProtoBuf proto, int sub, int tag) { try { - ProtoBuf subProto = - (proto != null && proto.has(sub)) ? proto.getProtoBuf(sub) : null; - return getProtoValueOrEmpty(subProto, tag); + return getProtoValueOrEmpty(getSubProtoOrNull(proto, sub), tag); } catch (ClassCastException e) { return ""; } } + /** Convenience method to get a subproto if the proto has it. */ + public static ProtoBuf getSubProtoOrNull(ProtoBuf proto, int sub) { + return (proto != null && proto.has(sub)) ? proto.getProtoBuf(sub) : null; + } + + /** + * Get an int with "tag" from the proto buffer. If the given field can't be + * retrieved, return the provided default value. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @param defaultValue The value to return if the field can't be retrieved. + * @return The result which should be an integer. + */ + public static int getProtoValueOrDefault(ProtoBuf proto, int tag, + int defaultValue) { + try { + return (proto != null && proto.has(tag)) + ? proto.getInt(tag) : defaultValue; + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (ClassCastException e) { + return defaultValue; + } + } + /** * Get an Int with "tag" from the proto buffer. * If the given field can't be retrieved, return 0. @@ -42,12 +69,25 @@ public final class ProtoBufUtil { * @return The result which should be an integer. */ public static int getProtoValueOrZero(ProtoBuf proto, int tag) { + return getProtoValueOrDefault(proto, tag, 0); + } + + /** + * Get an Long with "tag" from the proto buffer. + * If the given field can't be retrieved, return 0. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @return The result which should be an integer. + */ + public static long getProtoLongValueOrZero(ProtoBuf proto, int tag) { try { - return (proto != null && proto.has(tag)) ? proto.getInt(tag) : 0; + return (proto != null && proto.has(tag)) ? proto.getLong(tag) : 0L; } catch (IllegalArgumentException e) { - return 0; + return 0L; } catch (ClassCastException e) { - return 0; + return 0L; } } @@ -70,6 +110,39 @@ public final class ProtoBufUtil { } } + /** + * Reads a single protocol buffer from the given input stream. This method is + * provided where the client needs incremental access to the contents of a + * protocol buffer which contains a sequence of protocol buffers. + *

+ * Please use {@link #getInputStreamForProtoBufResponse} to obtain an input + * stream suitable for this method. + * + * @param umbrellaType the type of the "outer" protocol buffer containing + * the message to read + * @param is the stream to read the protocol buffer from + * @param result the result protocol buffer (must be empty, will be filled + * with the data read and the type will be set) + * @return the tag id of the message, -1 at the end of the stream + */ + public static int readNextProtoBuf(ProtoBufType umbrellaType, + InputStream is, ProtoBuf result) throws IOException { + long tagAndType = ProtoBuf.readVarInt(is, true /* permits EOF */); + if (tagAndType == -1) { + return -1; + } + + if ((tagAndType & 7) != ProtoBuf.WIRETYPE_LENGTH_DELIMITED) { + throw new IOException("Message expected"); + } + int tag = (int) (tagAndType >>> 3); + + result.setType((ProtoBufType) umbrellaType.getData(tag)); + int length = (int) ProtoBuf.readVarInt(is, false); + result.parse(is, length); + return tag; + } + /** * A wrapper for getProtoValueOrNegativeOne that drills into * a sub message returning the long value if it exists, returning -1 if it @@ -85,13 +158,80 @@ public final class ProtoBufUtil { public static long getSubProtoValueOrNegativeOne( ProtoBuf proto, int sub, int tag) { try { - ProtoBuf subProto = - (proto != null && proto.has(sub)) ? proto.getProtoBuf(sub) : null; - return getProtoValueOrNegativeOne(subProto, tag); + return getProtoValueOrNegativeOne(getSubProtoOrNull(proto, sub), tag); } catch (IllegalArgumentException e) { return -1; } catch (ClassCastException e) { return -1; } } + + /** + * A wrapper for {@link #getProtoValueOrDefault(ProtoBuf, int, int)} that + * drills into a sub message returning the int value if it exists, returning + * the given default if it does not. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @param sub The sub tag value that identifies which protocol buffer + * sub-field to retrieve. + * @param defaultValue The value to return if the field is not present. + * @return The result which should be a long. + */ + public static int getSubProtoValueOrDefault(ProtoBuf proto, int sub, int tag, + int defaultValue) { + try { + return getProtoValueOrDefault(getSubProtoOrNull(proto, sub), tag, + defaultValue); + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (ClassCastException e) { + return defaultValue; + } + } + + /** + * Creates a sub ProtoBuf of the given Protobuf and sets it. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * create. + * @return the sub ProtoBuf generated. + */ + public static ProtoBuf createProtoBuf(ProtoBuf proto, int tag) { + ProtoBuf child = proto.createGroup(tag); + proto.setProtoBuf(tag, child); + return child; + } + + /** + * Creates a sub ProtoBuf of the given Protobuf and adds it. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * add. + * @return the sub ProtoBuf generated. + */ + public static ProtoBuf addProtoBuf(ProtoBuf proto, int tag) { + ProtoBuf child = proto.createGroup(tag); + proto.addProtoBuf(tag, child); + return child; + } + + /** + * Writes the ProtoBuf to the given DataOutput. This is useful for unit + * tests. + * + * @param output The data output to write to. + * @param protoBuf The proto buffer. + */ + public static void writeProtoBufToOutput(DataOutput output, ProtoBuf protoBuf) + throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + protoBuf.outputTo(baos); + byte[] bytes = baos.toByteArray(); + output.writeInt(bytes.length); + output.write(bytes); + } } From bb7bbd7c9c1ecf5eb6055f34c87c64076b7877f2 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Mon, 2 Mar 2009 22:54:29 -0800 Subject: [PATCH 3/6] auto import from //depot/cupcake/@137055 --- .../google/common/io/protocol/ProtoBuf.java | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java index 8ebd1b9a33..ff7a324380 100644 --- a/src/com/google/common/io/protocol/ProtoBuf.java +++ b/src/com/google/common/io/protocol/ProtoBuf.java @@ -144,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. */ @@ -181,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) { @@ -196,7 +218,7 @@ public class ProtoBuf { ProtoBufType.TYPE_INT32)).longValue(); } - /** + /** * Returns the long value for the given tag. */ public long getLong(int tag) { @@ -238,7 +260,7 @@ public class ProtoBuf { return Double.longBitsToDouble(getLong(tag, index)); } - /** + /** * Returns the group or nested message for the given tag. */ public ProtoBuf getProtoBuf(int tag) { @@ -269,7 +291,7 @@ 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() { @@ -729,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. */ From a3cc5f17952e3c61e58215064a53f89edafd89d4 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 14:04:14 -0800 Subject: [PATCH 4/6] auto import from //depot/cupcake/@132589 --- .../google/common/io/protocol/ProtoBuf.java | 42 ++----------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java index ff7a324380..8ebd1b9a33 100644 --- a/src/com/google/common/io/protocol/ProtoBuf.java +++ b/src/com/google/common/io/protocol/ProtoBuf.java @@ -144,28 +144,6 @@ 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. */ @@ -203,7 +181,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) { @@ -218,7 +196,7 @@ public class ProtoBuf { ProtoBufType.TYPE_INT32)).longValue(); } - /** + /** * Returns the long value for the given tag. */ public long getLong(int tag) { @@ -260,7 +238,7 @@ public class ProtoBuf { return Double.longBitsToDouble(getLong(tag, index)); } - /** + /** * Returns the group or nested message for the given tag. */ public ProtoBuf getProtoBuf(int tag) { @@ -291,7 +269,7 @@ 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() { @@ -751,18 +729,6 @@ 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. */ From 778b51ced87cae8b2595918622f0a25d9aa691c2 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 18:28:35 -0800 Subject: [PATCH 5/6] auto import from //depot/cupcake/@135843 --- .../google/common/io/protocol/ProtoBuf.java | 1304 ----------------- .../common/io/protocol/ProtoBufType.java | 170 --- .../common/io/protocol/ProtoBufUtil.java | 237 --- .../google/common/io/protocol/package.html | 5 - 4 files changed, 1716 deletions(-) delete mode 100644 src/com/google/common/io/protocol/ProtoBuf.java delete mode 100644 src/com/google/common/io/protocol/ProtoBufType.java delete mode 100644 src/com/google/common/io/protocol/ProtoBufUtil.java delete mode 100644 src/com/google/common/io/protocol/package.html diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java deleted file mode 100644 index 8ebd1b9a33..0000000000 --- a/src/com/google/common/io/protocol/ProtoBuf.java +++ /dev/null @@ -1,1304 +0,0 @@ -// Copyright 2007 The Android Open Source Project -// All Rights Reserved. - -package com.google.common.io.protocol; - -import java.io.*; -import java.util.*; - -/** - * 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. - *

- * ProtoBuf instances may or may not reference a ProtoBufType instance, - * representing information from a corresponding .proto file, which defines tag - * data types. The type can only be set in the constructor, it cannot be - * changed later. - *

- * If the type is null, the ProtoBuffer should be used only for reading or - * as a local persistent storage buffer. An untyped Protocol Buffer must never - * be sent to a server. - *

- * If a ProtoBufType is set, unknown values are read from the stream and - * preserved, but it is not possible to add values for undefined tags using - * this API. Attempts to set undefined tags will result in an exception. - *

- * This class provides two different sets of access methods for simple and - * repeated tags. Simple access methods are has(tag), getXXX(tag), - * and setXXX(tag, value). Access methods for repeated tags are getCount(tag), - * getXXX(tag, index), remove(tag, index), insert(tag, index, value) and - * addXXX(tag, value). Note that both sets of methods can be used in both cases, - * 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. - * - */ - -public class ProtoBuf { - - public static final Boolean FALSE = new Boolean(false); - public static final Boolean TRUE = new Boolean(true); - - private static final String MSG_EOF = "Unexp.EOF"; - private static final String MSG_MISMATCH = "Type mismatch"; - private static final String MSG_UNSUPPORTED = "Unsupp.Type"; - - // 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; - 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; - - 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 Vector values = new Vector(); - - /** - * Wire types picked up on the wire or implied by setters (if no other - * type information is available. - */ - private final StringBuffer wireTypes = new StringBuffer(); - - /** - * Creates a protocol message according to the given description. The - * description is required if it is necessary to write the protocol buffer for - * data exchange with other systems relying on the .proto file. - */ - public ProtoBuf(ProtoBufType type) { - this.msgType = type; - } - - /** - * Clears all data stored in this ProtoBuf. - */ - public void clear() { - values.setSize(0); - wireTypes.setLength(0); - } - - /** - * Creates a new instance of the group with the given tag. - */ - public ProtoBuf createGroup(int tag) { - return new ProtoBuf((ProtoBufType) getType().getData(tag)); - } - - /** - * Appends the given (repeated) tag with the given boolean value. - */ - public void addBool(int tag, boolean value){ - insertBool(tag, getCount(tag), value); - } - - /** - * Appends the given (repeated) tag with the given byte[] value. - */ - public void addBytes(int tag, byte[] value){ - insertBytes(tag, getCount(tag), value); - } - - /** - * Appends the given (repeated) tag with the given int value. - */ - public void addInt(int tag, int value){ - insertInt(tag, getCount(tag), value); - } - - /** - * Appends the given (repeated) tag with the given long value. - */ - public void addLong(int tag, long value){ - 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. - */ - public void addProtoBuf(int tag, ProtoBuf value){ - insertProtoBuf(tag, getCount(tag), value); - } - - /** - * Appends the given (repeated) tag with the given String value. - */ - public void addString(int tag, String value){ - insertString(tag, getCount(tag), value); - } - - /** - * Returns the boolean value for the given tag. - */ - public boolean getBool(int tag) { - return ((Boolean) getObject(tag, ProtoBufType.TYPE_BOOL)) - .booleanValue(); - } - - /** - * Returns the boolean value for the given repeated tag at the given index. - */ - public boolean getBool(int tag, int index) { - return ((Boolean) getObject(tag, index, ProtoBufType.TYPE_BOOL)) - .booleanValue(); - } - - /** - * Returns the given string tag as byte array. - */ - public byte[] getBytes(int tag) { - return (byte[]) getObject(tag, ProtoBufType.TYPE_DATA); - } - - /** - * Returns the given repeated string tag at the given index as byte array. - */ - public byte[] getBytes(int tag, int index) { - return (byte[]) getObject(tag, index, ProtoBufType.TYPE_DATA); - } - - /** - * Returns the integer value for the given tag. - */ - public int getInt(int tag) { - 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) { - return (int) ((Long) getObject(tag, index, - ProtoBufType.TYPE_INT32)).longValue(); - } - - /** - * Returns the long value for the given tag. - */ - public long getLong(int tag) { - return ((Long) getObject(tag, ProtoBufType.TYPE_INT64)).longValue(); - } - - /** - * Returns the long value for the given repeated tag at the given index. - */ - public long getLong(int tag, int index) { - 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) { - return (ProtoBuf) getObject(tag, ProtoBufType.TYPE_GROUP); - } - - /** - * Returns the group or nested message for the given repeated tag at the given - * index. - */ - public ProtoBuf getProtoBuf(int tag, int index) { - return (ProtoBuf) getObject(tag, index, ProtoBufType.TYPE_GROUP); - } - - /** - * Returns the string value for a given tag converted to a Java String - * assuming UTF-8 encoding. - */ - public String getString(int tag) { - return (String) getObject(tag, ProtoBufType.TYPE_TEXT); - } - - /** - * Returns the string value for a given repeated tag at the given index - * converted to a Java String assuming UTF-8 encoding. - */ - public String getString(int tag, int index) { - 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 - * into account. - */ - public boolean has(int tag){ - return getCount(tag) > 0 || getDefault(tag) != null; - } - - /** - * Reads the contents of this ProtocolMessage from the given byte array. - * Currently, this is a shortcut for parse(new ByteArrayInputStream(data)). - * However, this may change in future versions for efficiency reasons. - * - * @param data the byte array the ProtocolMessage is read from - * @throws IOException if an unexpected "End of file" is encountered in - * the byte array - */ - public ProtoBuf parse(byte[] data) throws IOException { - parse(new ByteArrayInputStream(data), data.length); - return this; - } - - /** - * Reads the contents of this ProtocolMessage from the given stream. - * - * @param is the input stream providing the contents - * @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 - */ - - public ProtoBuf parse(InputStream is) throws IOException { - parse(is, Integer.MAX_VALUE); - return this; - } - - /** - * Reads the contents of this ProtocolMessage from the given stream, consuming - * at most the given number of bytes. - * - * @param is the input stream providing the contents - * @param available maximum number of bytes to read - * @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 - */ - public int parse(InputStream is, int available) throws IOException { - - clear(); - while (available > 0) { - long tagAndType = readVarInt(is, true /* permits EOF */); - - if (tagAndType == -1){ - break; - } - available -= getVarIntSize(tagAndType); - int wireType = ((int) tagAndType) & 0x07; - if (wireType == WIRETYPE_END_GROUP) { - break; - } - int tag = (int) (tagAndType >>> 3); - while (wireTypes.length() <= tag){ - wireTypes.append((char) ProtoBufType.TYPE_UNDEFINED); - } - wireTypes.setCharAt(tag, (char) wireType); - - // first step: decode tag value - Object value; - switch (wireType) { - case WIRETYPE_VARINT: - long v = readVarInt(is, false); - available -= getVarIntSize(v); - if (isZigZagEncodedType(tag)) { - v = zigZagDecode(v); - } - value = (v >= 0 && v < SMALL_NUMBERS.length) ? - SMALL_NUMBERS[(int) v] : new Long(v); - break; - - // also used for fixed values - case WIRETYPE_FIXED32: - case WIRETYPE_FIXED64: - v = 0; - int shift = 0; - int count = (wireType == WIRETYPE_FIXED32) ? 4 : 8; - available -= count; - - while (count-- > 0) { - long l = is.read(); - v |= l << shift; - shift += 8; - } - - value = (v >= 0 && v < SMALL_NUMBERS.length) - ? SMALL_NUMBERS[(int) v] - : new Long(v); - break; - - case WIRETYPE_LENGTH_DELIMITED: - int total = (int) readVarInt(is, false); - available -= getVarIntSize(total); - available -= total; - - if (getType(tag) == ProtoBufType.TYPE_MESSAGE) { - ProtoBuf msg = new ProtoBuf((ProtoBufType) msgType.getData(tag)); - msg.parse(is, total); - value = msg; - } else { - byte[] data = new byte[total]; - int pos = 0; - while (pos < total) { - count = is.read(data, pos, total - pos); - if (count <= 0) { - throw new IOException(MSG_EOF); - } - pos += count; - } - value = data; - } - break; - - case WIRETYPE_START_GROUP: - ProtoBuf group = new ProtoBuf(msgType == null - ? null - : ((ProtoBufType) msgType.getData(tag))); - available = group.parse(is, available); - value = group; - break; - - default: - throw new RuntimeException(MSG_UNSUPPORTED + wireType); - } - insertObject(tag, getCount(tag), value); - } - - if (available < 0){ - throw new IOException(); - } - - return available; - } - - /** - * Removes the tag value at the given index. - */ - public void remove(int tag, int index){ - int count = getCount(tag); - if (index >= count){ - throw new ArrayIndexOutOfBoundsException(); - } - if (count == 1){ - values.setElementAt(null, tag); - } else { - Vector v = (Vector) values.elementAt(tag); - v.removeElementAt(index); - } - } - - /** - * 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). - */ - public int getCount(int tag) { - if (tag >= values.size()){ - return 0; - } - Object o = values.elementAt(tag); - if (o == null){ - return 0; - } - return (o instanceof Vector) ? ((Vector) o).size() : 1; - } - - /** - * Returns the tag type of the given tag (one of the ProtoBufType.TYPE_XXX - * constants). If no ProtoBufType is set, the wire type is returned. If no - * wire type is available, the wire type is determined by looking at the - * tag value (making sure the wire type is consistent for all values). If - * no value is set, TYPE_UNDEFINED is returned. - */ - public int getType(int tag){ - int tagType = ProtoBufType.TYPE_UNDEFINED; - if (msgType != null){ - tagType = msgType.getType(tag); - } - - 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) - ? WIRETYPE_VARINT : WIRETYPE_LENGTH_DELIMITED; - } - - return tagType; - } - - /** - * Returns the number of bytes needed to store this protocol buffer - */ - public int getDataSize() { - int size = 0; - for (int tag = 0; tag <= maxTag(); tag++) { - for (int i = 0; i < getCount(tag); i++) { - size += getDataSize(tag, i); - } - } - return size; - } - - - /** - * Returns the size of the given value - */ - private int getDataSize(int tag, int i) { - int tagSize = getVarIntSize(tag << 3); - - switch(getWireType(tag)){ - case WIRETYPE_FIXED32: - return tagSize + 4; - case WIRETYPE_FIXED64: - return tagSize + 8; - case WIRETYPE_VARINT: - long value = getLong(tag, i); - if (isZigZagEncodedType(tag)) { - value = zigZagEncode(value); - } - return tagSize + getVarIntSize(value); - case WIRETYPE_START_GROUP: - // 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[]){ - contentSize = ((byte[]) o).length; - } else if (o instanceof String) { - contentSize = encodeUtf8((String) o, null, 0); - } else { - contentSize = ((ProtoBuf) o).getDataSize(); - } - - return tagSize + getVarIntSize(contentSize) + contentSize; - } - - /** - * Returns the number of bytes needed to encode the given value using - * WIRETYPE_VARINT - */ - private static int getVarIntSize(long i) { - if (i < 0) { - return 10; - } - int size = 1; - while (i >= 128) { - size++; - i >>= 7; - } - 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 - */ - public void outputTo(OutputStream os) throws IOException { - 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; - - 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(); - } - } - } - } - - /** - * Returns true if the given tag has a signed type that should be ZigZag- - * encoded on the wire. - * - * ZigZag encoding turns a signed number into - * a non-negative number by mapping negative input numbers to positive odd - * numbers in the output space, and positive input numbers to positive even - * numbers in the output space. This is useful because the wire format - * for protocol buffers requires a large number of bytes to encode - * negative integers, while positive integers take up a smaller number - * of bytes proportional to their magnitude. - */ - private boolean isZigZagEncodedType(int tag) { - int declaredType = getType(tag); - return declaredType == ProtoBufType.TYPE_SINT32 || - declaredType == ProtoBufType.TYPE_SINT64; - } - - /** - * Converts a signed number into a non-negative ZigZag-encoded number. - */ - private static long zigZagEncode(long v) { - v = ((v << 1) ^ -(v >>> 63)); - return v; - } - - /** - * Converts a non-negative ZigZag-encoded number back into a signed number. - */ - private static long zigZagDecode(long v) { - v = (v >>> 1) ^ -(v & 1); - return v; - } - - /** - * Writes this and nested protocol buffers to a byte array. - * - * @throws IOException thrown if there is problem writing the byte array - */ - public byte[] toByteArray() throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - outputTo(baos); - return baos.toByteArray(); - } - - /** - * Returns the largest tag id used in this message (to simplify testing). - */ - public int maxTag() { - return values.size() - 1; - } - - /** - * Sets the given tag to the given boolean value. - */ - public void setBool(int tag, boolean value) { - setObject(tag, value ? TRUE : FALSE); - } - - /** - * Sets the given tag to the given data bytes. - */ - public void setBytes(int tag, byte[] value) { - setObject(tag, value); - } - - /** - * Sets the given tag to the given integer value. - */ - public void setInt(int tag, int value) { - setLong(tag, value); - } - - /** - * Sets the given tag to the given long value. - */ - public void setLong(int tag, long value) { - setObject(tag, value >= 0 && value < SMALL_NUMBERS.length - ? 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. - */ - public void setProtoBuf(int tag, ProtoBuf pb) { - setObject(tag, pb); - } - - /** - * Sets the given tag to the given String value. - */ - public void setString(int tag, String value) { - setObject(tag, value); - } - - /** - * Inserts the given boolean value for the given tag at the given index. - */ - public void insertBool(int tag, int index, boolean value) { - insertObject(tag, index, value ? TRUE : FALSE); - } - - /** - * Inserts the given byte array value for the given tag at the given index. - */ - public void insertBytes(int tag, int index, byte[] value) { - insertObject(tag, index, value); - } - - /** - * Inserts the given int value for the given tag at the given index. - */ - public void insertInt(int tag, int index, int value) { - insertLong(tag, index, value); - } - - /** - * 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 >= 0 && value < SMALL_NUMBERS.length - ? 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. - */ - public void insertProtoBuf(int tag, int index, ProtoBuf pb) { - insertObject(tag, index, pb); - } - - /** - * Inserts the given string value for the given tag at the given index. - */ - public void insertString(int tag, int index, String value) { - insertObject(tag, index, value); - } - - // ----------------- private stuff below this line ------------------------ - - private void assertTypeMatch(int tag, Object object){ - int tagType = getType(tag); - if (tagType == ProtoBufType.TYPE_UNDEFINED && msgType == null) { - return; - } - - if (object instanceof Boolean) { - if (tagType == ProtoBufType.TYPE_BOOL - || tagType == WIRETYPE_VARINT) { - return; - } - } else if (object instanceof Long) { - switch(tagType){ - case WIRETYPE_FIXED32: - case WIRETYPE_FIXED64: - case WIRETYPE_VARINT: - case ProtoBufType.TYPE_BOOL: - case ProtoBufType.TYPE_ENUM: - case ProtoBufType.TYPE_FIXED32: - case ProtoBufType.TYPE_FIXED64: - case ProtoBufType.TYPE_INT32: - case ProtoBufType.TYPE_INT64: - case ProtoBufType.TYPE_SFIXED32: - case ProtoBufType.TYPE_SFIXED64: - case ProtoBufType.TYPE_UINT32: - 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[]){ - switch (tagType){ - case WIRETYPE_LENGTH_DELIMITED: - case ProtoBufType.TYPE_DATA: - case ProtoBufType.TYPE_MESSAGE: - case ProtoBufType.TYPE_TEXT: - case ProtoBufType.TYPE_BYTES: - return; - } - } else if (object instanceof ProtoBuf) { - switch (tagType){ - case WIRETYPE_LENGTH_DELIMITED: - case WIRETYPE_START_GROUP: - case ProtoBufType.TYPE_DATA: - case ProtoBufType.TYPE_GROUP: - case ProtoBufType.TYPE_MESSAGE: - if (msgType == null || msgType.getData(tag) == null || - ((ProtoBuf) object).msgType == null || - ((ProtoBuf) object).msgType == msgType.getData(tag)) { - return; - } - } - } else if (object instanceof String){ - switch (tagType){ - case WIRETYPE_LENGTH_DELIMITED: - case ProtoBufType.TYPE_DATA: - case ProtoBufType.TYPE_TEXT: - case ProtoBufType.TYPE_STRING: - return; - } - } - throw new IllegalArgumentException(MSG_MISMATCH + " type:" + msgType + - " tag:" + tag); - } - - /** - * Returns the default value for the given tag. - */ - private Object getDefault(int tag){ - - switch(getType(tag)){ - case ProtoBufType.TYPE_UNDEFINED: - case ProtoBufType.TYPE_GROUP: - case ProtoBufType.TYPE_MESSAGE: - return null; - default: - return msgType.getData(tag); - } - } - - /** - * Returns the indicated value converted to the given type. - * - * @throws ArrayIndexOutOfBoundsException for invalid tags and indices - * @throws IllegalArgumentException if count is greater than one. - */ - private Object getObject(int tag, int desiredType) { - - int count = getCount(tag); - - if (count == 0){ - return getDefault(tag); - } - - if (count > 1){ - throw new IllegalArgumentException(); - } - - return getObject(tag, 0, desiredType); - } - - /** - * Returns the indicated value converted to the given type. - * - * @throws ArrayIndexOutOfBoundsException for invalid tags and indices - */ - private Object getObject(int tag, int index, int desiredType) { - - if (index >= getCount(tag)) { - throw new ArrayIndexOutOfBoundsException(); - } - - Object o = values.elementAt(tag); - - Vector v = null; - if (o instanceof Vector) { - v = (Vector) o; - o = v.elementAt(index); - } - - Object o2 = convert(o, desiredType); - - if (o2 != o && o != null) { - if (v == null){ - setObject(tag, o2); - } else { - v.setElementAt(o2, index); - } - } - - return o2; - } - - /** - * Returns the wire type for the given tag. Calls getType() internally, - * so a wire type should be found for all non-empty tags, even if no - * message type is set and the tag was not previously read. - */ - private final int getWireType(int tag) { - - int tagType = getType(tag); - - switch (tagType) { - case WIRETYPE_VARINT: - case WIRETYPE_FIXED32: - case WIRETYPE_FIXED64: - case WIRETYPE_LENGTH_DELIMITED: - case WIRETYPE_START_GROUP: - case ProtoBufType.TYPE_UNDEFINED: - return tagType; - - case ProtoBufType.TYPE_BOOL: - case ProtoBufType.TYPE_INT32: - case ProtoBufType.TYPE_INT64: - case ProtoBufType.TYPE_UINT32: - case ProtoBufType.TYPE_UINT64: - case ProtoBufType.TYPE_SINT32: - case ProtoBufType.TYPE_SINT64: - case ProtoBufType.TYPE_ENUM: - return WIRETYPE_VARINT; - case ProtoBufType.TYPE_DATA: - case ProtoBufType.TYPE_MESSAGE: - case ProtoBufType.TYPE_TEXT: - case ProtoBufType.TYPE_BYTES: - case ProtoBufType.TYPE_STRING: - return WIRETYPE_LENGTH_DELIMITED; - case ProtoBufType.TYPE_DOUBLE: - case ProtoBufType.TYPE_FIXED64: - case ProtoBufType.TYPE_SFIXED64: - return WIRETYPE_FIXED64; - case ProtoBufType.TYPE_FLOAT: - case ProtoBufType.TYPE_FIXED32: - case ProtoBufType.TYPE_SFIXED32: - return WIRETYPE_FIXED32; - case ProtoBufType.TYPE_GROUP: - return WIRETYPE_START_GROUP; - default: - throw new RuntimeException(MSG_UNSUPPORTED + ':' + msgType + '/' + - tag + '/' + tagType); - } - } - - /** - * Inserts a value. - */ - private void insertObject(int tag, int index, Object o) { - assertTypeMatch(tag, o); - - int count = getCount(tag); - - if (count == 0) { - setObject(tag, o); - } else { - Object curr = values.elementAt(tag); - Vector v; - if (curr instanceof Vector) { - v = (Vector) curr; - } else { - v = new Vector(); - v.addElement(curr); - values.setElementAt(v, tag); - } - v.insertElementAt(o, index); - } - } - - /** - * 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 Object convert(Object obj, int tagType) { - switch (tagType) { - case ProtoBufType.TYPE_UNDEFINED: - return obj; - - case ProtoBufType.TYPE_BOOL: - if (obj instanceof Boolean) { - return obj; - } - switch ((int) ((Long) obj).longValue()) { - case 0: - return FALSE; - case 1: - return TRUE; - default: - throw new IllegalArgumentException(MSG_MISMATCH); - } - case ProtoBufType.TYPE_FIXED32: - case ProtoBufType.TYPE_FIXED64: - case ProtoBufType.TYPE_INT32: - case ProtoBufType.TYPE_INT64: - case ProtoBufType.TYPE_SFIXED32: - case ProtoBufType.TYPE_SFIXED64: - case ProtoBufType.TYPE_SINT32: - case ProtoBufType.TYPE_SINT64: - if (obj instanceof Boolean) { - return SMALL_NUMBERS[((Boolean) obj).booleanValue() ? 1 : 0]; - } - return obj; - case ProtoBufType.TYPE_DATA: - case ProtoBufType.TYPE_BYTES: - if (obj instanceof String) { - return encodeUtf8((String) obj); - } else if (obj instanceof ProtoBuf) { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - try { - ((ProtoBuf) obj).outputTo(buf); - return buf.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e.toString()); - } - } - return obj; - case ProtoBufType.TYPE_TEXT: - case ProtoBufType.TYPE_STRING: - if (obj instanceof byte[]) { - byte[] data = (byte[]) obj; - return decodeUtf8(data, 0, data.length, true); - } - return obj; - case ProtoBufType.TYPE_GROUP: - case ProtoBufType.TYPE_MESSAGE: - if (obj instanceof byte[]) { - try { - return new ProtoBuf(null).parse((byte[]) obj); - } catch (IOException e) { - throw new RuntimeException(e.toString()); - } - } - return obj; - default: - // default includes FLOAT and DOUBLE - throw new RuntimeException(MSG_UNSUPPORTED); - } - } - - /** - * Reads a variable-size integer (up to 10 bytes for 64 bit) from the - * given input stream. - * - * @param is the stream to read from - * @param permitEOF if true, -1 is returned when EOF is reached instead of - * throwing an IOException - * @return the integer value read from the stream, or -1 if EOF is - * reached and permitEOF is true - * @throws IOException thrown for underlying IO issues and if EOF - * is reached and permitEOF is false - */ - static long readVarInt(InputStream is, boolean permitEOF) throws IOException { - - long result = 0; - int shift = 0; - - // max 10 byte wire format for 64 bit integer (7 bit data per byte) - - for (int i = 0; i < VARINT_MAX_BYTES; i++) { - int in = is.read(); - - if (in == -1) { - if (i == 0 && permitEOF) { - return -1; - } else { - throw new IOException("EOF"); - } - } - result |= ((long) (in & 0x07f)) << shift; - - if ((in & 0x80) == 0){ - break; // get out early - } - - shift += 7; - } - return result; - } - - /** - * Internal helper method to set a (single) value. Overwrites all existing - * values. - */ - private void setObject(int tag, Object o) { - if (values.size() <= tag) { - values.setSize(tag + 1); - } - if (o != null) { - assertTypeMatch(tag, o); - } - values.setElementAt(o, tag); - } - - /** - * Write a variable-size integer to the given output stream. - */ - static void writeVarInt(OutputStream os, long value) throws IOException { - for (int i = 0; i < VARINT_MAX_BYTES; i++) { - - int toWrite = (int) (value & 0x7f); - - value >>>= 7; - - if (value == 0) { - os.write(toWrite); - break; - } else { - os.write(toWrite | 0x080); - } - } - } - - /** - * Returns a byte array containing the given string, encoded as UTF-8. The - * returned byte array contains at least s.length() bytes and at most - * 4 * s.length() bytes. UTF-16 surrogates are transcoded to UTF-8. - * - * @param s input string to be encoded - * @return UTF-8 encoded input string - */ - static byte[] encodeUtf8(String s) { - int len = encodeUtf8(s, null, 0); - byte[] result = new byte[len]; - encodeUtf8(s, result, 0); - return result; - } - - /** - * 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 - * 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) - if (((codeLo & 0xfc00) ^ (code & 0x0fc00)) == 0x0400){ - - i += 1; - - int codeHi; - if ((codeLo & 0xfc00) == 0x0d800){ - codeHi = codeLo; - codeLo = code; - } else { - codeHi = code; - } - code = (((codeHi & 0x3ff) << 10) | (codeLo & 0x3ff)) + 0x10000; - } - } - if (code <= 0x007f) { - if (buf != null){ - buf[pos] = (byte) code; - } - pos += 1; - } else if (code <= 0x07FF) { - // non-ASCII <= 0x7FF - if (buf != null){ - buf[pos] = (byte) (0xc0 | (code >> 6)); - buf[pos + 1] = (byte) (0x80 | (code & 0x3F)); - } - pos += 2; - } else if (code <= 0xFFFF){ - // 0x7FF < code <= 0xFFFF - if (buf != null){ - buf[pos] = (byte) ((0xe0 | (code >> 12))); - buf[pos + 1] = (byte) ((0x80 | ((code >> 6) & 0x3F))); - buf[pos + 2] = (byte) ((0x80 | (code & 0x3F))); - } - pos += 3; - } else { - if (buf != null){ - buf[pos] = (byte) ((0xf0 | (code >> 18))); - buf[pos + 1] = (byte) ((0x80 | ((code >> 12) & 0x3F))); - buf[pos + 2] = (byte) ((0x80 | ((code >> 6) & 0x3F))); - buf[pos + 3] = (byte) ((0x80 | (code & 0x3F))); - } - 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 - * @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 - * UTF-8 codes - * @return the string containing the UTF-8 decoding result - */ - 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 - if (!tolerant){ - throw new IllegalArgumentException("Invalid UTF8"); - } - sb.append((char) b); - } else { - int border = 0xe0; - int count = 1; - int minCode = 128; - int mask = 0x01f; - while (b >= border){ - border = (border >> 1) | 0x80; - 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){ - if (!tolerant){ - throw new IllegalArgumentException("Invalid UTF8"); - } - // otherwise, assume zeroes - } else { - if (!tolerant && (data[pos] & 0xc0) != 0x80){ - throw new IllegalArgumentException("Invalid UTF8"); - } - code |= (data[pos++] & 0x3f); // six bit - } - } - - // illegal code or surrogate code - if (!tolerant && code < minCode || (code >= 0xd800 && code <= 0xdfff)){ - throw new IllegalArgumentException("Invalid UTF8"); - } - - if (code <= 0x0ffff){ - sb.append((char) code); - } else { // surrogate UTF16 - code -= 0x10000; - sb.append((char) (0xd800 | (code >> 10))); // high 10 bit - sb.append((char) (0xdc00 | (code & 0x3ff))); // low 10 bit - } - } - } - return sb.toString(); - } - -} diff --git a/src/com/google/common/io/protocol/ProtoBufType.java b/src/com/google/common/io/protocol/ProtoBufType.java deleted file mode 100644 index 4b6408e888..0000000000 --- a/src/com/google/common/io/protocol/ProtoBufType.java +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2007 Google Inc. -// All Rights Reserved. - -package com.google.common.io.protocol; - -import java.util.*; - -/** - * 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! - public static final int TYPE_UNDEFINED = 16; - public static final int TYPE_DOUBLE = 17; - public static final int TYPE_FLOAT = 18; - public static final int TYPE_INT64 = 19; - public static final int TYPE_UINT64 = 20; - public static final int TYPE_INT32 = 21; - public static final int TYPE_FIXED64 = 22; - public static final int TYPE_FIXED32 = 23; - public static final int TYPE_BOOL = 24; - public static final int TYPE_DATA = 25; - public static final int TYPE_GROUP = 26; - public static final int TYPE_MESSAGE = 27; - public static final int TYPE_TEXT = 28; - public static final int TYPE_UINT32 = 29; - public static final int TYPE_ENUM = 30; - public static final int TYPE_SFIXED32 = 31; - public static final int TYPE_SFIXED64 = 32; - - // new protobuf 2 types - public static final int TYPE_SINT32 = 33; - public static final int TYPE_SINT64 = 34; - public static final int TYPE_BYTES = 35; - public static final int TYPE_STRING = 36; - - public static final int MASK_TYPE = 0x0ff; - public static final int MASK_MODIFIER = 0x0ff00; - - public static final int REQUIRED = 0x100; - public static final int OPTIONAL = 0x200; - public static final int REPEATED = 0x400; - - private final StringBuffer types = new StringBuffer(); - private final Vector data = new Vector(); - private final String typeName; - - /** - * Empty constructor. - */ - public ProtoBufType() { - typeName = null; - } - - /** - * Constructor including a type name for debugging purposes. - */ - public ProtoBufType(String typeName) { - this.typeName = typeName; - } - - /** - * Adds a tag description. The data parameter contains the group definition - * for group elements and the default value for regular elements. - * - * @param optionsAndType any legal combination (bitwise or) of REQUIRED - * or OPTIONAL and REPEATED and one of the TYPE_ - * constants - * @param tag the tag id - * @param data the type for group elements (or the default value for - * regular elements in future versions) - * @return this is returned to permit cascading - */ - public ProtoBufType addElement(int optionsAndType, int tag, Object data) { - while (types.length() <= tag) { - types.append((char) TYPE_UNDEFINED); - this.data.addElement(null); - } - types.setCharAt(tag, (char) optionsAndType); - this.data.setElementAt(data, tag); - - 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) { - 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) { - return (tag < 0 || tag >= types.length()) - ? (OPTIONAL | REPEATED) - : (types.charAt(tag) & MASK_MODIFIER); - } - - /** - * Returns the data associated to a given tag (either the default value for - * regular elements or a ProtoBufType for groups and messages). For undefined - * tags, null is returned. - */ - public Object getData(int tag) { - 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 typeName; - } - - /** - * {@inheritDoc} - *

Two ProtoBufTypes are equals if the fields types are the same. - */ - public boolean equals(Object object) { - if (null == object) { - // trivial check - return false; - } else if (this == object) { - // trivial check - return true; - } else if (this.getClass() != object.getClass()) { - // different class - return false; - } - ProtoBufType other = (ProtoBufType) object; - - return stringEquals(types, other.types); - } - - /** - * {@inheritDoc} - */ - public int hashCode() { - if (types != null) { - return types.hashCode(); - } else { - 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 deleted file mode 100644 index 72e1bca9ed..0000000000 --- a/src/com/google/common/io/protocol/ProtoBufUtil.java +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2008 Google Inc. All Rights Reserved. - -package com.google.common.io.protocol; - -import java.io.*; - -/** - * Utility functions for dealing with ProtoBuf objects consolidated from - * previous spot implementations across the codebase. - * - */ -public final class ProtoBufUtil { - private ProtoBufUtil() { - } - - /** Convenience method to return a string value from of a proto or "". */ - public static String getProtoValueOrEmpty(ProtoBuf proto, int tag) { - try { - return (proto != null && proto.has(tag)) ? proto.getString(tag) : ""; - } catch (ClassCastException e) { - return ""; - } - } - - /** Convenience method to return a string value from of a sub-proto or "". */ - public static String getSubProtoValueOrEmpty( - ProtoBuf proto, int sub, int tag) { - try { - return getProtoValueOrEmpty(getSubProtoOrNull(proto, sub), tag); - } catch (ClassCastException e) { - return ""; - } - } - - /** Convenience method to get a subproto if the proto has it. */ - public static ProtoBuf getSubProtoOrNull(ProtoBuf proto, int sub) { - return (proto != null && proto.has(sub)) ? proto.getProtoBuf(sub) : null; - } - - /** - * Get an int with "tag" from the proto buffer. If the given field can't be - * retrieved, return the provided default value. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * retrieve. - * @param defaultValue The value to return if the field can't be retrieved. - * @return The result which should be an integer. - */ - public static int getProtoValueOrDefault(ProtoBuf proto, int tag, - int defaultValue) { - try { - return (proto != null && proto.has(tag)) - ? proto.getInt(tag) : defaultValue; - } catch (IllegalArgumentException e) { - return defaultValue; - } catch (ClassCastException e) { - return defaultValue; - } - } - - /** - * Get an Int with "tag" from the proto buffer. - * If the given field can't be retrieved, return 0. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * retrieve. - * @return The result which should be an integer. - */ - public static int getProtoValueOrZero(ProtoBuf proto, int tag) { - return getProtoValueOrDefault(proto, tag, 0); - } - - /** - * Get an Long with "tag" from the proto buffer. - * If the given field can't be retrieved, return 0. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * retrieve. - * @return The result which should be an integer. - */ - public static long getProtoLongValueOrZero(ProtoBuf proto, int tag) { - try { - return (proto != null && proto.has(tag)) ? proto.getLong(tag) : 0L; - } catch (IllegalArgumentException e) { - return 0L; - } catch (ClassCastException e) { - return 0L; - } - } - - /** - * Get an Int with "tag" from the proto buffer. - * If the given field can't be retrieved, return -1. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * retrieve. - * @return The result which should be a long. - */ - public static long getProtoValueOrNegativeOne(ProtoBuf proto, int tag) { - try { - return (proto != null && proto.has(tag)) ? proto.getLong(tag) : -1; - } catch (IllegalArgumentException e) { - return -1; - } catch (ClassCastException e) { - return -1; - } - } - - /** - * Reads a single protocol buffer from the given input stream. This method is - * provided where the client needs incremental access to the contents of a - * protocol buffer which contains a sequence of protocol buffers. - *

- * Please use {@link #getInputStreamForProtoBufResponse} to obtain an input - * stream suitable for this method. - * - * @param umbrellaType the type of the "outer" protocol buffer containing - * the message to read - * @param is the stream to read the protocol buffer from - * @param result the result protocol buffer (must be empty, will be filled - * with the data read and the type will be set) - * @return the tag id of the message, -1 at the end of the stream - */ - public static int readNextProtoBuf(ProtoBufType umbrellaType, - InputStream is, ProtoBuf result) throws IOException { - long tagAndType = ProtoBuf.readVarInt(is, true /* permits EOF */); - if (tagAndType == -1) { - return -1; - } - - if ((tagAndType & 7) != ProtoBuf.WIRETYPE_LENGTH_DELIMITED) { - throw new IOException("Message expected"); - } - int tag = (int) (tagAndType >>> 3); - - result.setType((ProtoBufType) umbrellaType.getData(tag)); - int length = (int) ProtoBuf.readVarInt(is, false); - result.parse(is, length); - return tag; - } - - /** - * A wrapper for getProtoValueOrNegativeOne that drills into - * a sub message returning the long value if it exists, returning -1 if it - * does not. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * retrieve. - * @param sub The sub tag value that identifies which protocol buffer - * sub-field to retrieve.n - * @return The result which should be a long. - */ - public static long getSubProtoValueOrNegativeOne( - ProtoBuf proto, int sub, int tag) { - try { - return getProtoValueOrNegativeOne(getSubProtoOrNull(proto, sub), tag); - } catch (IllegalArgumentException e) { - return -1; - } catch (ClassCastException e) { - return -1; - } - } - - /** - * A wrapper for {@link #getProtoValueOrDefault(ProtoBuf, int, int)} that - * drills into a sub message returning the int value if it exists, returning - * the given default if it does not. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * retrieve. - * @param sub The sub tag value that identifies which protocol buffer - * sub-field to retrieve. - * @param defaultValue The value to return if the field is not present. - * @return The result which should be a long. - */ - public static int getSubProtoValueOrDefault(ProtoBuf proto, int sub, int tag, - int defaultValue) { - try { - return getProtoValueOrDefault(getSubProtoOrNull(proto, sub), tag, - defaultValue); - } catch (IllegalArgumentException e) { - return defaultValue; - } catch (ClassCastException e) { - return defaultValue; - } - } - - /** - * Creates a sub ProtoBuf of the given Protobuf and sets it. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * create. - * @return the sub ProtoBuf generated. - */ - public static ProtoBuf createProtoBuf(ProtoBuf proto, int tag) { - ProtoBuf child = proto.createGroup(tag); - proto.setProtoBuf(tag, child); - return child; - } - - /** - * Creates a sub ProtoBuf of the given Protobuf and adds it. - * - * @param proto The proto buffer. - * @param tag The tag value that identifies which protocol buffer field to - * add. - * @return the sub ProtoBuf generated. - */ - public static ProtoBuf addProtoBuf(ProtoBuf proto, int tag) { - ProtoBuf child = proto.createGroup(tag); - proto.addProtoBuf(tag, child); - return child; - } - - /** - * Writes the ProtoBuf to the given DataOutput. This is useful for unit - * tests. - * - * @param output The data output to write to. - * @param protoBuf The proto buffer. - */ - public static void writeProtoBufToOutput(DataOutput output, ProtoBuf protoBuf) - throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - protoBuf.outputTo(baos); - byte[] bytes = baos.toByteArray(); - output.writeInt(bytes.length); - output.write(bytes); - } -} diff --git a/src/com/google/common/io/protocol/package.html b/src/com/google/common/io/protocol/package.html deleted file mode 100644 index 1c9bf9dad8..0000000000 --- a/src/com/google/common/io/protocol/package.html +++ /dev/null @@ -1,5 +0,0 @@ - - - {@hide} - - From 82155ace012637e95eb2a4c61377105028aa1aad Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:30:31 -0800 Subject: [PATCH 6/6] auto import from //depot/cupcake/@135843 --- .../google/common/io/protocol/ProtoBuf.java | 1338 +++++++++++++++++ .../common/io/protocol/ProtoBufType.java | 170 +++ .../common/io/protocol/ProtoBufUtil.java | 237 +++ .../google/common/io/protocol/package.html | 5 + 4 files changed, 1750 insertions(+) create mode 100644 src/com/google/common/io/protocol/ProtoBuf.java create mode 100644 src/com/google/common/io/protocol/ProtoBufType.java create mode 100644 src/com/google/common/io/protocol/ProtoBufUtil.java create mode 100644 src/com/google/common/io/protocol/package.html diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java new file mode 100644 index 0000000000..ff7a324380 --- /dev/null +++ b/src/com/google/common/io/protocol/ProtoBuf.java @@ -0,0 +1,1338 @@ +// Copyright 2007 The Android Open Source Project +// All Rights Reserved. + +package com.google.common.io.protocol; + +import java.io.*; +import java.util.*; + +/** + * 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. + *

+ * ProtoBuf instances may or may not reference a ProtoBufType instance, + * representing information from a corresponding .proto file, which defines tag + * data types. The type can only be set in the constructor, it cannot be + * changed later. + *

+ * If the type is null, the ProtoBuffer should be used only for reading or + * as a local persistent storage buffer. An untyped Protocol Buffer must never + * be sent to a server. + *

+ * If a ProtoBufType is set, unknown values are read from the stream and + * preserved, but it is not possible to add values for undefined tags using + * this API. Attempts to set undefined tags will result in an exception. + *

+ * This class provides two different sets of access methods for simple and + * repeated tags. Simple access methods are has(tag), getXXX(tag), + * and setXXX(tag, value). Access methods for repeated tags are getCount(tag), + * getXXX(tag, index), remove(tag, index), insert(tag, index, value) and + * addXXX(tag, value). Note that both sets of methods can be used in both cases, + * 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. + * + */ + +public class ProtoBuf { + + public static final Boolean FALSE = new Boolean(false); + public static final Boolean TRUE = new Boolean(true); + + private static final String MSG_EOF = "Unexp.EOF"; + private static final String MSG_MISMATCH = "Type mismatch"; + private static final String MSG_UNSUPPORTED = "Unsupp.Type"; + + // 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; + 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; + + 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 Vector values = new Vector(); + + /** + * Wire types picked up on the wire or implied by setters (if no other + * type information is available. + */ + private final StringBuffer wireTypes = new StringBuffer(); + + /** + * Creates a protocol message according to the given description. The + * description is required if it is necessary to write the protocol buffer for + * data exchange with other systems relying on the .proto file. + */ + public ProtoBuf(ProtoBufType type) { + this.msgType = type; + } + + /** + * Clears all data stored in this ProtoBuf. + */ + public void clear() { + values.setSize(0); + wireTypes.setLength(0); + } + + /** + * Creates a new instance of the group with the given tag. + */ + public ProtoBuf createGroup(int tag) { + return new ProtoBuf((ProtoBufType) getType().getData(tag)); + } + + /** + * Appends the given (repeated) tag with the given boolean value. + */ + public void addBool(int tag, boolean value){ + insertBool(tag, getCount(tag), value); + } + + /** + * Appends the given (repeated) tag with the given byte[] value. + */ + public void addBytes(int tag, byte[] value){ + insertBytes(tag, getCount(tag), value); + } + + /** + * Appends the given (repeated) tag with the given int value. + */ + public void addInt(int tag, int value){ + insertInt(tag, getCount(tag), value); + } + + /** + * Appends the given (repeated) tag with the given long value. + */ + public void addLong(int tag, long value){ + 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. + */ + public void addProtoBuf(int tag, ProtoBuf value){ + 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. + */ + public void addString(int tag, String value){ + insertString(tag, getCount(tag), value); + } + + /** + * Returns the boolean value for the given tag. + */ + public boolean getBool(int tag) { + return ((Boolean) getObject(tag, ProtoBufType.TYPE_BOOL)) + .booleanValue(); + } + + /** + * Returns the boolean value for the given repeated tag at the given index. + */ + public boolean getBool(int tag, int index) { + return ((Boolean) getObject(tag, index, ProtoBufType.TYPE_BOOL)) + .booleanValue(); + } + + /** + * Returns the given string tag as byte array. + */ + public byte[] getBytes(int tag) { + return (byte[]) getObject(tag, ProtoBufType.TYPE_DATA); + } + + /** + * Returns the given repeated string tag at the given index as byte array. + */ + public byte[] getBytes(int tag, int index) { + return (byte[]) getObject(tag, index, ProtoBufType.TYPE_DATA); + } + + /** + * Returns the integer value for the given tag. + */ + public int getInt(int tag) { + 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) { + return (int) ((Long) getObject(tag, index, + ProtoBufType.TYPE_INT32)).longValue(); + } + + /** + * Returns the long value for the given tag. + */ + public long getLong(int tag) { + return ((Long) getObject(tag, ProtoBufType.TYPE_INT64)).longValue(); + } + + /** + * Returns the long value for the given repeated tag at the given index. + */ + public long getLong(int tag, int index) { + 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) { + return (ProtoBuf) getObject(tag, ProtoBufType.TYPE_GROUP); + } + + /** + * Returns the group or nested message for the given repeated tag at the given + * index. + */ + public ProtoBuf getProtoBuf(int tag, int index) { + return (ProtoBuf) getObject(tag, index, ProtoBufType.TYPE_GROUP); + } + + /** + * Returns the string value for a given tag converted to a Java String + * assuming UTF-8 encoding. + */ + public String getString(int tag) { + return (String) getObject(tag, ProtoBufType.TYPE_TEXT); + } + + /** + * Returns the string value for a given repeated tag at the given index + * converted to a Java String assuming UTF-8 encoding. + */ + public String getString(int tag, int index) { + 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 + * into account. + */ + public boolean has(int tag){ + return getCount(tag) > 0 || getDefault(tag) != null; + } + + /** + * Reads the contents of this ProtocolMessage from the given byte array. + * Currently, this is a shortcut for parse(new ByteArrayInputStream(data)). + * However, this may change in future versions for efficiency reasons. + * + * @param data the byte array the ProtocolMessage is read from + * @throws IOException if an unexpected "End of file" is encountered in + * the byte array + */ + public ProtoBuf parse(byte[] data) throws IOException { + parse(new ByteArrayInputStream(data), data.length); + return this; + } + + /** + * Reads the contents of this ProtocolMessage from the given stream. + * + * @param is the input stream providing the contents + * @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 + */ + + public ProtoBuf parse(InputStream is) throws IOException { + parse(is, Integer.MAX_VALUE); + return this; + } + + /** + * Reads the contents of this ProtocolMessage from the given stream, consuming + * at most the given number of bytes. + * + * @param is the input stream providing the contents + * @param available maximum number of bytes to read + * @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 + */ + public int parse(InputStream is, int available) throws IOException { + + clear(); + while (available > 0) { + long tagAndType = readVarInt(is, true /* permits EOF */); + + if (tagAndType == -1){ + break; + } + available -= getVarIntSize(tagAndType); + int wireType = ((int) tagAndType) & 0x07; + if (wireType == WIRETYPE_END_GROUP) { + break; + } + int tag = (int) (tagAndType >>> 3); + while (wireTypes.length() <= tag){ + wireTypes.append((char) ProtoBufType.TYPE_UNDEFINED); + } + wireTypes.setCharAt(tag, (char) wireType); + + // first step: decode tag value + Object value; + switch (wireType) { + case WIRETYPE_VARINT: + long v = readVarInt(is, false); + available -= getVarIntSize(v); + if (isZigZagEncodedType(tag)) { + v = zigZagDecode(v); + } + value = (v >= 0 && v < SMALL_NUMBERS.length) ? + SMALL_NUMBERS[(int) v] : new Long(v); + break; + + // also used for fixed values + case WIRETYPE_FIXED32: + case WIRETYPE_FIXED64: + v = 0; + int shift = 0; + int count = (wireType == WIRETYPE_FIXED32) ? 4 : 8; + available -= count; + + while (count-- > 0) { + long l = is.read(); + v |= l << shift; + shift += 8; + } + + value = (v >= 0 && v < SMALL_NUMBERS.length) + ? SMALL_NUMBERS[(int) v] + : new Long(v); + break; + + case WIRETYPE_LENGTH_DELIMITED: + int total = (int) readVarInt(is, false); + available -= getVarIntSize(total); + available -= total; + + if (getType(tag) == ProtoBufType.TYPE_MESSAGE) { + ProtoBuf msg = new ProtoBuf((ProtoBufType) msgType.getData(tag)); + msg.parse(is, total); + value = msg; + } else { + byte[] data = new byte[total]; + int pos = 0; + while (pos < total) { + count = is.read(data, pos, total - pos); + if (count <= 0) { + throw new IOException(MSG_EOF); + } + pos += count; + } + value = data; + } + break; + + case WIRETYPE_START_GROUP: + ProtoBuf group = new ProtoBuf(msgType == null + ? null + : ((ProtoBufType) msgType.getData(tag))); + available = group.parse(is, available); + value = group; + break; + + default: + throw new RuntimeException(MSG_UNSUPPORTED + wireType); + } + insertObject(tag, getCount(tag), value); + } + + if (available < 0){ + throw new IOException(); + } + + return available; + } + + /** + * Removes the tag value at the given index. + */ + public void remove(int tag, int index){ + int count = getCount(tag); + if (index >= count){ + throw new ArrayIndexOutOfBoundsException(); + } + if (count == 1){ + values.setElementAt(null, tag); + } else { + Vector v = (Vector) values.elementAt(tag); + v.removeElementAt(index); + } + } + + /** + * 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). + */ + public int getCount(int tag) { + if (tag >= values.size()){ + return 0; + } + Object o = values.elementAt(tag); + if (o == null){ + return 0; + } + return (o instanceof Vector) ? ((Vector) o).size() : 1; + } + + /** + * Returns the tag type of the given tag (one of the ProtoBufType.TYPE_XXX + * constants). If no ProtoBufType is set, the wire type is returned. If no + * wire type is available, the wire type is determined by looking at the + * tag value (making sure the wire type is consistent for all values). If + * no value is set, TYPE_UNDEFINED is returned. + */ + public int getType(int tag){ + int tagType = ProtoBufType.TYPE_UNDEFINED; + if (msgType != null){ + tagType = msgType.getType(tag); + } + + 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) + ? WIRETYPE_VARINT : WIRETYPE_LENGTH_DELIMITED; + } + + return tagType; + } + + /** + * Returns the number of bytes needed to store this protocol buffer + */ + public int getDataSize() { + int size = 0; + for (int tag = 0; tag <= maxTag(); tag++) { + for (int i = 0; i < getCount(tag); i++) { + size += getDataSize(tag, i); + } + } + return size; + } + + + /** + * Returns the size of the given value + */ + private int getDataSize(int tag, int i) { + int tagSize = getVarIntSize(tag << 3); + + switch(getWireType(tag)){ + case WIRETYPE_FIXED32: + return tagSize + 4; + case WIRETYPE_FIXED64: + return tagSize + 8; + case WIRETYPE_VARINT: + long value = getLong(tag, i); + if (isZigZagEncodedType(tag)) { + value = zigZagEncode(value); + } + return tagSize + getVarIntSize(value); + case WIRETYPE_START_GROUP: + // 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[]){ + contentSize = ((byte[]) o).length; + } else if (o instanceof String) { + contentSize = encodeUtf8((String) o, null, 0); + } else { + contentSize = ((ProtoBuf) o).getDataSize(); + } + + return tagSize + getVarIntSize(contentSize) + contentSize; + } + + /** + * Returns the number of bytes needed to encode the given value using + * WIRETYPE_VARINT + */ + private static int getVarIntSize(long i) { + if (i < 0) { + return 10; + } + int size = 1; + while (i >= 128) { + size++; + i >>= 7; + } + 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 + */ + public void outputTo(OutputStream os) throws IOException { + 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; + + 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(); + } + } + } + } + + /** + * Returns true if the given tag has a signed type that should be ZigZag- + * encoded on the wire. + * + * ZigZag encoding turns a signed number into + * a non-negative number by mapping negative input numbers to positive odd + * numbers in the output space, and positive input numbers to positive even + * numbers in the output space. This is useful because the wire format + * for protocol buffers requires a large number of bytes to encode + * negative integers, while positive integers take up a smaller number + * of bytes proportional to their magnitude. + */ + private boolean isZigZagEncodedType(int tag) { + int declaredType = getType(tag); + return declaredType == ProtoBufType.TYPE_SINT32 || + declaredType == ProtoBufType.TYPE_SINT64; + } + + /** + * Converts a signed number into a non-negative ZigZag-encoded number. + */ + private static long zigZagEncode(long v) { + v = ((v << 1) ^ -(v >>> 63)); + return v; + } + + /** + * Converts a non-negative ZigZag-encoded number back into a signed number. + */ + private static long zigZagDecode(long v) { + v = (v >>> 1) ^ -(v & 1); + return v; + } + + /** + * Writes this and nested protocol buffers to a byte array. + * + * @throws IOException thrown if there is problem writing the byte array + */ + public byte[] toByteArray() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + outputTo(baos); + return baos.toByteArray(); + } + + /** + * Returns the largest tag id used in this message (to simplify testing). + */ + public int maxTag() { + return values.size() - 1; + } + + /** + * Sets the given tag to the given boolean value. + */ + public void setBool(int tag, boolean value) { + setObject(tag, value ? TRUE : FALSE); + } + + /** + * Sets the given tag to the given data bytes. + */ + public void setBytes(int tag, byte[] value) { + setObject(tag, value); + } + + /** + * Sets the given tag to the given integer value. + */ + public void setInt(int tag, int value) { + setLong(tag, value); + } + + /** + * Sets the given tag to the given long value. + */ + public void setLong(int tag, long value) { + setObject(tag, value >= 0 && value < SMALL_NUMBERS.length + ? 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. + */ + public void setProtoBuf(int tag, ProtoBuf pb) { + 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. + */ + public void setString(int tag, String value) { + setObject(tag, value); + } + + /** + * Inserts the given boolean value for the given tag at the given index. + */ + public void insertBool(int tag, int index, boolean value) { + insertObject(tag, index, value ? TRUE : FALSE); + } + + /** + * Inserts the given byte array value for the given tag at the given index. + */ + public void insertBytes(int tag, int index, byte[] value) { + insertObject(tag, index, value); + } + + /** + * Inserts the given int value for the given tag at the given index. + */ + public void insertInt(int tag, int index, int value) { + insertLong(tag, index, value); + } + + /** + * 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 >= 0 && value < SMALL_NUMBERS.length + ? 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. + */ + public void insertProtoBuf(int tag, int index, ProtoBuf pb) { + insertObject(tag, index, pb); + } + + /** + * Inserts the given string value for the given tag at the given index. + */ + public void insertString(int tag, int index, String value) { + insertObject(tag, index, value); + } + + // ----------------- private stuff below this line ------------------------ + + private void assertTypeMatch(int tag, Object object){ + int tagType = getType(tag); + if (tagType == ProtoBufType.TYPE_UNDEFINED && msgType == null) { + return; + } + + if (object instanceof Boolean) { + if (tagType == ProtoBufType.TYPE_BOOL + || tagType == WIRETYPE_VARINT) { + return; + } + } else if (object instanceof Long) { + switch(tagType){ + case WIRETYPE_FIXED32: + case WIRETYPE_FIXED64: + case WIRETYPE_VARINT: + case ProtoBufType.TYPE_BOOL: + case ProtoBufType.TYPE_ENUM: + case ProtoBufType.TYPE_FIXED32: + case ProtoBufType.TYPE_FIXED64: + case ProtoBufType.TYPE_INT32: + case ProtoBufType.TYPE_INT64: + case ProtoBufType.TYPE_SFIXED32: + case ProtoBufType.TYPE_SFIXED64: + case ProtoBufType.TYPE_UINT32: + 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[]){ + switch (tagType){ + case WIRETYPE_LENGTH_DELIMITED: + case ProtoBufType.TYPE_DATA: + case ProtoBufType.TYPE_MESSAGE: + case ProtoBufType.TYPE_TEXT: + case ProtoBufType.TYPE_BYTES: + return; + } + } else if (object instanceof ProtoBuf) { + switch (tagType){ + case WIRETYPE_LENGTH_DELIMITED: + case WIRETYPE_START_GROUP: + case ProtoBufType.TYPE_DATA: + case ProtoBufType.TYPE_GROUP: + case ProtoBufType.TYPE_MESSAGE: + if (msgType == null || msgType.getData(tag) == null || + ((ProtoBuf) object).msgType == null || + ((ProtoBuf) object).msgType == msgType.getData(tag)) { + return; + } + } + } else if (object instanceof String){ + switch (tagType){ + case WIRETYPE_LENGTH_DELIMITED: + case ProtoBufType.TYPE_DATA: + case ProtoBufType.TYPE_TEXT: + case ProtoBufType.TYPE_STRING: + return; + } + } + throw new IllegalArgumentException(MSG_MISMATCH + " type:" + msgType + + " tag:" + tag); + } + + /** + * Returns the default value for the given tag. + */ + private Object getDefault(int tag){ + + switch(getType(tag)){ + case ProtoBufType.TYPE_UNDEFINED: + case ProtoBufType.TYPE_GROUP: + case ProtoBufType.TYPE_MESSAGE: + return null; + default: + return msgType.getData(tag); + } + } + + /** + * Returns the indicated value converted to the given type. + * + * @throws ArrayIndexOutOfBoundsException for invalid tags and indices + * @throws IllegalArgumentException if count is greater than one. + */ + private Object getObject(int tag, int desiredType) { + + int count = getCount(tag); + + if (count == 0){ + return getDefault(tag); + } + + if (count > 1){ + throw new IllegalArgumentException(); + } + + return getObject(tag, 0, desiredType); + } + + /** + * Returns the indicated value converted to the given type. + * + * @throws ArrayIndexOutOfBoundsException for invalid tags and indices + */ + private Object getObject(int tag, int index, int desiredType) { + + if (index >= getCount(tag)) { + throw new ArrayIndexOutOfBoundsException(); + } + + Object o = values.elementAt(tag); + + Vector v = null; + if (o instanceof Vector) { + v = (Vector) o; + o = v.elementAt(index); + } + + Object o2 = convert(o, desiredType); + + if (o2 != o && o != null) { + if (v == null){ + setObject(tag, o2); + } else { + v.setElementAt(o2, index); + } + } + + return o2; + } + + /** + * Returns the wire type for the given tag. Calls getType() internally, + * so a wire type should be found for all non-empty tags, even if no + * message type is set and the tag was not previously read. + */ + private final int getWireType(int tag) { + + int tagType = getType(tag); + + switch (tagType) { + case WIRETYPE_VARINT: + case WIRETYPE_FIXED32: + case WIRETYPE_FIXED64: + case WIRETYPE_LENGTH_DELIMITED: + case WIRETYPE_START_GROUP: + case ProtoBufType.TYPE_UNDEFINED: + return tagType; + + case ProtoBufType.TYPE_BOOL: + case ProtoBufType.TYPE_INT32: + case ProtoBufType.TYPE_INT64: + case ProtoBufType.TYPE_UINT32: + case ProtoBufType.TYPE_UINT64: + case ProtoBufType.TYPE_SINT32: + case ProtoBufType.TYPE_SINT64: + case ProtoBufType.TYPE_ENUM: + return WIRETYPE_VARINT; + case ProtoBufType.TYPE_DATA: + case ProtoBufType.TYPE_MESSAGE: + case ProtoBufType.TYPE_TEXT: + case ProtoBufType.TYPE_BYTES: + case ProtoBufType.TYPE_STRING: + return WIRETYPE_LENGTH_DELIMITED; + case ProtoBufType.TYPE_DOUBLE: + case ProtoBufType.TYPE_FIXED64: + case ProtoBufType.TYPE_SFIXED64: + return WIRETYPE_FIXED64; + case ProtoBufType.TYPE_FLOAT: + case ProtoBufType.TYPE_FIXED32: + case ProtoBufType.TYPE_SFIXED32: + return WIRETYPE_FIXED32; + case ProtoBufType.TYPE_GROUP: + return WIRETYPE_START_GROUP; + default: + throw new RuntimeException(MSG_UNSUPPORTED + ':' + msgType + '/' + + tag + '/' + tagType); + } + } + + /** + * Inserts a value. + */ + private void insertObject(int tag, int index, Object o) { + assertTypeMatch(tag, o); + + int count = getCount(tag); + + if (count == 0) { + setObject(tag, o); + } else { + Object curr = values.elementAt(tag); + Vector v; + if (curr instanceof Vector) { + v = (Vector) curr; + } else { + v = new Vector(); + v.addElement(curr); + values.setElementAt(v, tag); + } + v.insertElementAt(o, index); + } + } + + /** + * 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 Object convert(Object obj, int tagType) { + switch (tagType) { + case ProtoBufType.TYPE_UNDEFINED: + return obj; + + case ProtoBufType.TYPE_BOOL: + if (obj instanceof Boolean) { + return obj; + } + switch ((int) ((Long) obj).longValue()) { + case 0: + return FALSE; + case 1: + return TRUE; + default: + throw new IllegalArgumentException(MSG_MISMATCH); + } + case ProtoBufType.TYPE_FIXED32: + case ProtoBufType.TYPE_FIXED64: + case ProtoBufType.TYPE_INT32: + case ProtoBufType.TYPE_INT64: + case ProtoBufType.TYPE_SFIXED32: + case ProtoBufType.TYPE_SFIXED64: + case ProtoBufType.TYPE_SINT32: + case ProtoBufType.TYPE_SINT64: + if (obj instanceof Boolean) { + return SMALL_NUMBERS[((Boolean) obj).booleanValue() ? 1 : 0]; + } + return obj; + case ProtoBufType.TYPE_DATA: + case ProtoBufType.TYPE_BYTES: + if (obj instanceof String) { + return encodeUtf8((String) obj); + } else if (obj instanceof ProtoBuf) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + try { + ((ProtoBuf) obj).outputTo(buf); + return buf.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e.toString()); + } + } + return obj; + case ProtoBufType.TYPE_TEXT: + case ProtoBufType.TYPE_STRING: + if (obj instanceof byte[]) { + byte[] data = (byte[]) obj; + return decodeUtf8(data, 0, data.length, true); + } + return obj; + case ProtoBufType.TYPE_GROUP: + case ProtoBufType.TYPE_MESSAGE: + if (obj instanceof byte[]) { + try { + return new ProtoBuf(null).parse((byte[]) obj); + } catch (IOException e) { + throw new RuntimeException(e.toString()); + } + } + return obj; + default: + // default includes FLOAT and DOUBLE + throw new RuntimeException(MSG_UNSUPPORTED); + } + } + + /** + * Reads a variable-size integer (up to 10 bytes for 64 bit) from the + * given input stream. + * + * @param is the stream to read from + * @param permitEOF if true, -1 is returned when EOF is reached instead of + * throwing an IOException + * @return the integer value read from the stream, or -1 if EOF is + * reached and permitEOF is true + * @throws IOException thrown for underlying IO issues and if EOF + * is reached and permitEOF is false + */ + static long readVarInt(InputStream is, boolean permitEOF) throws IOException { + + long result = 0; + int shift = 0; + + // max 10 byte wire format for 64 bit integer (7 bit data per byte) + + for (int i = 0; i < VARINT_MAX_BYTES; i++) { + int in = is.read(); + + if (in == -1) { + if (i == 0 && permitEOF) { + return -1; + } else { + throw new IOException("EOF"); + } + } + result |= ((long) (in & 0x07f)) << shift; + + if ((in & 0x80) == 0){ + break; // get out early + } + + shift += 7; + } + return result; + } + + /** + * Internal helper method to set a (single) value. Overwrites all existing + * values. + */ + private void setObject(int tag, Object o) { + if (values.size() <= tag) { + values.setSize(tag + 1); + } + if (o != null) { + assertTypeMatch(tag, o); + } + values.setElementAt(o, tag); + } + + /** + * Write a variable-size integer to the given output stream. + */ + static void writeVarInt(OutputStream os, long value) throws IOException { + for (int i = 0; i < VARINT_MAX_BYTES; i++) { + + int toWrite = (int) (value & 0x7f); + + value >>>= 7; + + if (value == 0) { + os.write(toWrite); + break; + } else { + os.write(toWrite | 0x080); + } + } + } + + /** + * Returns a byte array containing the given string, encoded as UTF-8. The + * returned byte array contains at least s.length() bytes and at most + * 4 * s.length() bytes. UTF-16 surrogates are transcoded to UTF-8. + * + * @param s input string to be encoded + * @return UTF-8 encoded input string + */ + static byte[] encodeUtf8(String s) { + int len = encodeUtf8(s, null, 0); + byte[] result = new byte[len]; + encodeUtf8(s, result, 0); + return result; + } + + /** + * 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 + * 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) + if (((codeLo & 0xfc00) ^ (code & 0x0fc00)) == 0x0400){ + + i += 1; + + int codeHi; + if ((codeLo & 0xfc00) == 0x0d800){ + codeHi = codeLo; + codeLo = code; + } else { + codeHi = code; + } + code = (((codeHi & 0x3ff) << 10) | (codeLo & 0x3ff)) + 0x10000; + } + } + if (code <= 0x007f) { + if (buf != null){ + buf[pos] = (byte) code; + } + pos += 1; + } else if (code <= 0x07FF) { + // non-ASCII <= 0x7FF + if (buf != null){ + buf[pos] = (byte) (0xc0 | (code >> 6)); + buf[pos + 1] = (byte) (0x80 | (code & 0x3F)); + } + pos += 2; + } else if (code <= 0xFFFF){ + // 0x7FF < code <= 0xFFFF + if (buf != null){ + buf[pos] = (byte) ((0xe0 | (code >> 12))); + buf[pos + 1] = (byte) ((0x80 | ((code >> 6) & 0x3F))); + buf[pos + 2] = (byte) ((0x80 | (code & 0x3F))); + } + pos += 3; + } else { + if (buf != null){ + buf[pos] = (byte) ((0xf0 | (code >> 18))); + buf[pos + 1] = (byte) ((0x80 | ((code >> 12) & 0x3F))); + buf[pos + 2] = (byte) ((0x80 | ((code >> 6) & 0x3F))); + buf[pos + 3] = (byte) ((0x80 | (code & 0x3F))); + } + 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 + * @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 + * UTF-8 codes + * @return the string containing the UTF-8 decoding result + */ + 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 + if (!tolerant){ + throw new IllegalArgumentException("Invalid UTF8"); + } + sb.append((char) b); + } else { + int border = 0xe0; + int count = 1; + int minCode = 128; + int mask = 0x01f; + while (b >= border){ + border = (border >> 1) | 0x80; + 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){ + if (!tolerant){ + throw new IllegalArgumentException("Invalid UTF8"); + } + // otherwise, assume zeroes + } else { + if (!tolerant && (data[pos] & 0xc0) != 0x80){ + throw new IllegalArgumentException("Invalid UTF8"); + } + code |= (data[pos++] & 0x3f); // six bit + } + } + + // illegal code or surrogate code + if (!tolerant && code < minCode || (code >= 0xd800 && code <= 0xdfff)){ + throw new IllegalArgumentException("Invalid UTF8"); + } + + if (code <= 0x0ffff){ + sb.append((char) code); + } else { // surrogate UTF16 + code -= 0x10000; + sb.append((char) (0xd800 | (code >> 10))); // high 10 bit + sb.append((char) (0xdc00 | (code & 0x3ff))); // low 10 bit + } + } + } + return sb.toString(); + } + +} diff --git a/src/com/google/common/io/protocol/ProtoBufType.java b/src/com/google/common/io/protocol/ProtoBufType.java new file mode 100644 index 0000000000..4b6408e888 --- /dev/null +++ b/src/com/google/common/io/protocol/ProtoBufType.java @@ -0,0 +1,170 @@ +// Copyright 2007 Google Inc. +// All Rights Reserved. + +package com.google.common.io.protocol; + +import java.util.*; + +/** + * 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! + public static final int TYPE_UNDEFINED = 16; + public static final int TYPE_DOUBLE = 17; + public static final int TYPE_FLOAT = 18; + public static final int TYPE_INT64 = 19; + public static final int TYPE_UINT64 = 20; + public static final int TYPE_INT32 = 21; + public static final int TYPE_FIXED64 = 22; + public static final int TYPE_FIXED32 = 23; + public static final int TYPE_BOOL = 24; + public static final int TYPE_DATA = 25; + public static final int TYPE_GROUP = 26; + public static final int TYPE_MESSAGE = 27; + public static final int TYPE_TEXT = 28; + public static final int TYPE_UINT32 = 29; + public static final int TYPE_ENUM = 30; + public static final int TYPE_SFIXED32 = 31; + public static final int TYPE_SFIXED64 = 32; + + // new protobuf 2 types + public static final int TYPE_SINT32 = 33; + public static final int TYPE_SINT64 = 34; + public static final int TYPE_BYTES = 35; + public static final int TYPE_STRING = 36; + + public static final int MASK_TYPE = 0x0ff; + public static final int MASK_MODIFIER = 0x0ff00; + + public static final int REQUIRED = 0x100; + public static final int OPTIONAL = 0x200; + public static final int REPEATED = 0x400; + + private final StringBuffer types = new StringBuffer(); + private final Vector data = new Vector(); + private final String typeName; + + /** + * Empty constructor. + */ + public ProtoBufType() { + typeName = null; + } + + /** + * Constructor including a type name for debugging purposes. + */ + public ProtoBufType(String typeName) { + this.typeName = typeName; + } + + /** + * Adds a tag description. The data parameter contains the group definition + * for group elements and the default value for regular elements. + * + * @param optionsAndType any legal combination (bitwise or) of REQUIRED + * or OPTIONAL and REPEATED and one of the TYPE_ + * constants + * @param tag the tag id + * @param data the type for group elements (or the default value for + * regular elements in future versions) + * @return this is returned to permit cascading + */ + public ProtoBufType addElement(int optionsAndType, int tag, Object data) { + while (types.length() <= tag) { + types.append((char) TYPE_UNDEFINED); + this.data.addElement(null); + } + types.setCharAt(tag, (char) optionsAndType); + this.data.setElementAt(data, tag); + + 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) { + 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) { + return (tag < 0 || tag >= types.length()) + ? (OPTIONAL | REPEATED) + : (types.charAt(tag) & MASK_MODIFIER); + } + + /** + * Returns the data associated to a given tag (either the default value for + * regular elements or a ProtoBufType for groups and messages). For undefined + * tags, null is returned. + */ + public Object getData(int tag) { + 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 typeName; + } + + /** + * {@inheritDoc} + *

Two ProtoBufTypes are equals if the fields types are the same. + */ + public boolean equals(Object object) { + if (null == object) { + // trivial check + return false; + } else if (this == object) { + // trivial check + return true; + } else if (this.getClass() != object.getClass()) { + // different class + return false; + } + ProtoBufType other = (ProtoBufType) object; + + return stringEquals(types, other.types); + } + + /** + * {@inheritDoc} + */ + public int hashCode() { + if (types != null) { + return types.hashCode(); + } else { + 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 new file mode 100644 index 0000000000..72e1bca9ed --- /dev/null +++ b/src/com/google/common/io/protocol/ProtoBufUtil.java @@ -0,0 +1,237 @@ +// Copyright 2008 Google Inc. All Rights Reserved. + +package com.google.common.io.protocol; + +import java.io.*; + +/** + * Utility functions for dealing with ProtoBuf objects consolidated from + * previous spot implementations across the codebase. + * + */ +public final class ProtoBufUtil { + private ProtoBufUtil() { + } + + /** Convenience method to return a string value from of a proto or "". */ + public static String getProtoValueOrEmpty(ProtoBuf proto, int tag) { + try { + return (proto != null && proto.has(tag)) ? proto.getString(tag) : ""; + } catch (ClassCastException e) { + return ""; + } + } + + /** Convenience method to return a string value from of a sub-proto or "". */ + public static String getSubProtoValueOrEmpty( + ProtoBuf proto, int sub, int tag) { + try { + return getProtoValueOrEmpty(getSubProtoOrNull(proto, sub), tag); + } catch (ClassCastException e) { + return ""; + } + } + + /** Convenience method to get a subproto if the proto has it. */ + public static ProtoBuf getSubProtoOrNull(ProtoBuf proto, int sub) { + return (proto != null && proto.has(sub)) ? proto.getProtoBuf(sub) : null; + } + + /** + * Get an int with "tag" from the proto buffer. If the given field can't be + * retrieved, return the provided default value. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @param defaultValue The value to return if the field can't be retrieved. + * @return The result which should be an integer. + */ + public static int getProtoValueOrDefault(ProtoBuf proto, int tag, + int defaultValue) { + try { + return (proto != null && proto.has(tag)) + ? proto.getInt(tag) : defaultValue; + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (ClassCastException e) { + return defaultValue; + } + } + + /** + * Get an Int with "tag" from the proto buffer. + * If the given field can't be retrieved, return 0. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @return The result which should be an integer. + */ + public static int getProtoValueOrZero(ProtoBuf proto, int tag) { + return getProtoValueOrDefault(proto, tag, 0); + } + + /** + * Get an Long with "tag" from the proto buffer. + * If the given field can't be retrieved, return 0. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @return The result which should be an integer. + */ + public static long getProtoLongValueOrZero(ProtoBuf proto, int tag) { + try { + return (proto != null && proto.has(tag)) ? proto.getLong(tag) : 0L; + } catch (IllegalArgumentException e) { + return 0L; + } catch (ClassCastException e) { + return 0L; + } + } + + /** + * Get an Int with "tag" from the proto buffer. + * If the given field can't be retrieved, return -1. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @return The result which should be a long. + */ + public static long getProtoValueOrNegativeOne(ProtoBuf proto, int tag) { + try { + return (proto != null && proto.has(tag)) ? proto.getLong(tag) : -1; + } catch (IllegalArgumentException e) { + return -1; + } catch (ClassCastException e) { + return -1; + } + } + + /** + * Reads a single protocol buffer from the given input stream. This method is + * provided where the client needs incremental access to the contents of a + * protocol buffer which contains a sequence of protocol buffers. + *

+ * Please use {@link #getInputStreamForProtoBufResponse} to obtain an input + * stream suitable for this method. + * + * @param umbrellaType the type of the "outer" protocol buffer containing + * the message to read + * @param is the stream to read the protocol buffer from + * @param result the result protocol buffer (must be empty, will be filled + * with the data read and the type will be set) + * @return the tag id of the message, -1 at the end of the stream + */ + public static int readNextProtoBuf(ProtoBufType umbrellaType, + InputStream is, ProtoBuf result) throws IOException { + long tagAndType = ProtoBuf.readVarInt(is, true /* permits EOF */); + if (tagAndType == -1) { + return -1; + } + + if ((tagAndType & 7) != ProtoBuf.WIRETYPE_LENGTH_DELIMITED) { + throw new IOException("Message expected"); + } + int tag = (int) (tagAndType >>> 3); + + result.setType((ProtoBufType) umbrellaType.getData(tag)); + int length = (int) ProtoBuf.readVarInt(is, false); + result.parse(is, length); + return tag; + } + + /** + * A wrapper for getProtoValueOrNegativeOne that drills into + * a sub message returning the long value if it exists, returning -1 if it + * does not. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @param sub The sub tag value that identifies which protocol buffer + * sub-field to retrieve.n + * @return The result which should be a long. + */ + public static long getSubProtoValueOrNegativeOne( + ProtoBuf proto, int sub, int tag) { + try { + return getProtoValueOrNegativeOne(getSubProtoOrNull(proto, sub), tag); + } catch (IllegalArgumentException e) { + return -1; + } catch (ClassCastException e) { + return -1; + } + } + + /** + * A wrapper for {@link #getProtoValueOrDefault(ProtoBuf, int, int)} that + * drills into a sub message returning the int value if it exists, returning + * the given default if it does not. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * retrieve. + * @param sub The sub tag value that identifies which protocol buffer + * sub-field to retrieve. + * @param defaultValue The value to return if the field is not present. + * @return The result which should be a long. + */ + public static int getSubProtoValueOrDefault(ProtoBuf proto, int sub, int tag, + int defaultValue) { + try { + return getProtoValueOrDefault(getSubProtoOrNull(proto, sub), tag, + defaultValue); + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (ClassCastException e) { + return defaultValue; + } + } + + /** + * Creates a sub ProtoBuf of the given Protobuf and sets it. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * create. + * @return the sub ProtoBuf generated. + */ + public static ProtoBuf createProtoBuf(ProtoBuf proto, int tag) { + ProtoBuf child = proto.createGroup(tag); + proto.setProtoBuf(tag, child); + return child; + } + + /** + * Creates a sub ProtoBuf of the given Protobuf and adds it. + * + * @param proto The proto buffer. + * @param tag The tag value that identifies which protocol buffer field to + * add. + * @return the sub ProtoBuf generated. + */ + public static ProtoBuf addProtoBuf(ProtoBuf proto, int tag) { + ProtoBuf child = proto.createGroup(tag); + proto.addProtoBuf(tag, child); + return child; + } + + /** + * Writes the ProtoBuf to the given DataOutput. This is useful for unit + * tests. + * + * @param output The data output to write to. + * @param protoBuf The proto buffer. + */ + public static void writeProtoBufToOutput(DataOutput output, ProtoBuf protoBuf) + throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + protoBuf.outputTo(baos); + byte[] bytes = baos.toByteArray(); + output.writeInt(bytes.length); + output.write(bytes); + } +} diff --git a/src/com/google/common/io/protocol/package.html b/src/com/google/common/io/protocol/package.html new file mode 100644 index 0000000000..1c9bf9dad8 --- /dev/null +++ b/src/com/google/common/io/protocol/package.html @@ -0,0 +1,5 @@ + + + {@hide} + +