From 15bf10329ff5ed4b039918df3f053eaf67851e1b Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Thu, 19 Feb 2009 10:57:31 -0800 Subject: auto import from //branches/cupcake/...@132276 --- src/com/google/common/io/protocol/ProtoBuf.java | 143 ++++++++++++++---- .../google/common/io/protocol/ProtoBufType.java | 50 ++++++- .../google/common/io/protocol/ProtoBufUtil.java | 160 +++++++++++++++++++-- 3 files changed, 312 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/com/google/common/io/protocol/ProtoBuf.java b/src/com/google/common/io/protocol/ProtoBuf.java index f2af32f..8ebd1b9 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(); /** @@ -124,6 +124,20 @@ public class ProtoBuf { } /** + * 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){ @@ -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. */ @@ -235,6 +277,20 @@ public class ProtoBuf { } /** + * 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. @@ -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 1aec8f9..4b6408e 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 077bd3d..72e1bca 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; } } @@ -71,6 +111,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 * does not. @@ -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); + } } -- cgit v1.1