| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.util.proto; |
| |
| import android.annotation.IntDef; |
| import android.annotation.LongDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| |
| /** |
| * Base utility class for protobuf streams. |
| * |
| * Contains a set of constants and methods used in generated code for |
| * {@link ProtoOutputStream}. |
| * |
| * @hide |
| */ |
| @android.ravenwood.annotation.RavenwoodKeepWholeClass |
| public class ProtoStream { |
| |
| /** |
| * A protobuf wire type. All application-level types are represented using |
| * varint, fixed64, length-delimited and fixed32 wire types. The start-group |
| * and end-group types are unused in modern protobuf versions (proto2 and proto3), |
| * but are included here for completeness. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({ |
| WIRE_TYPE_VARINT, |
| WIRE_TYPE_FIXED64, |
| WIRE_TYPE_LENGTH_DELIMITED, |
| WIRE_TYPE_START_GROUP, |
| WIRE_TYPE_END_GROUP, |
| WIRE_TYPE_FIXED32 |
| }) |
| public @interface WireType {} |
| |
| /** |
| * Application-level protobuf field types, as would be used in a .proto file. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| @Retention(RetentionPolicy.SOURCE) |
| @LongDef({ |
| FIELD_TYPE_UNKNOWN, |
| FIELD_TYPE_DOUBLE, |
| FIELD_TYPE_FLOAT, |
| FIELD_TYPE_INT64, |
| FIELD_TYPE_UINT64, |
| FIELD_TYPE_INT32, |
| FIELD_TYPE_FIXED64, |
| FIELD_TYPE_FIXED32, |
| FIELD_TYPE_BOOL, |
| FIELD_TYPE_STRING, |
| FIELD_TYPE_MESSAGE, |
| FIELD_TYPE_BYTES, |
| FIELD_TYPE_UINT32, |
| FIELD_TYPE_ENUM, |
| FIELD_TYPE_SFIXED32, |
| FIELD_TYPE_SFIXED64, |
| FIELD_TYPE_SINT32, |
| FIELD_TYPE_SINT64, |
| }) |
| public @interface FieldType {} |
| |
| |
| /** |
| * Represents the cardinality of a protobuf field. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| @Retention(RetentionPolicy.SOURCE) |
| @LongDef({ |
| FIELD_COUNT_UNKNOWN, |
| FIELD_COUNT_SINGLE, |
| FIELD_COUNT_REPEATED, |
| FIELD_COUNT_PACKED, |
| }) |
| public @interface FieldCount {} |
| |
| /** |
| * Number of bits to shift the field number to form a tag. |
| * |
| * <pre> |
| * // Reading a field number from a tag. |
| * int fieldNumber = tag >>> FIELD_ID_SHIFT; |
| * |
| * // Building a tag from a field number and a wire type. |
| * int tag = (fieldNumber << FIELD_ID_SHIFT) | wireType; |
| * </pre> |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int FIELD_ID_SHIFT = 3; |
| |
| /** |
| * Mask to select the wire type from a tag. |
| * |
| * <pre> |
| * // Reading a wire type from a tag. |
| * int wireType = tag & WIRE_TYPE_MASK; |
| * |
| * // Building a tag from a field number and a wire type. |
| * int tag = (fieldNumber << FIELD_ID_SHIFT) | wireType; |
| * </pre> |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1; |
| |
| /** |
| * Mask to select the field id from a tag. |
| * @hide (not used by anything, and not actually useful, because you also want |
| * to shift when you mask the field id). |
| */ |
| public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK; |
| |
| /** |
| * Varint wire type code. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int WIRE_TYPE_VARINT = 0; |
| |
| /** |
| * Fixed64 wire type code. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int WIRE_TYPE_FIXED64 = 1; |
| |
| /** |
| * Length delimited wire type code. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int WIRE_TYPE_LENGTH_DELIMITED = 2; |
| |
| /** |
| * Start group wire type code. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int WIRE_TYPE_START_GROUP = 3; |
| |
| /** |
| * End group wire type code. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int WIRE_TYPE_END_GROUP = 4; |
| |
| /** |
| * Fixed32 wire type code. |
| * |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final int WIRE_TYPE_FIXED32 = 5; |
| |
| /** |
| * Position of the field type in a (long) fieldId. |
| */ |
| public static final int FIELD_TYPE_SHIFT = 32; |
| |
| /** |
| * Mask for the field types stored in a fieldId. Leaves a whole |
| * byte for future expansion, even though there are currently only 17 types. |
| */ |
| public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Not a real field type. |
| * @hide |
| */ |
| public static final long FIELD_TYPE_UNKNOWN = 0; |
| |
| |
| /* |
| * The FIELD_TYPE_ constants are copied from |
| * external/protobuf/src/google/protobuf/descriptor.h directly, so no |
| * extra mapping needs to be maintained in this case. |
| */ |
| |
| /** |
| * Field type code for double fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, double) |
| * ProtoOutputStream.write(long, double)} method. |
| */ |
| public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for float fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, float) |
| * ProtoOutputStream.write(long, float)} method. |
| */ |
| public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for int64 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, long) |
| * ProtoOutputStream.write(long, long)} method. |
| */ |
| public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for uint64 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, long) |
| * ProtoOutputStream.write(long, long)} method. |
| */ |
| public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for int32 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, int) |
| * ProtoOutputStream.write(long, int)} method. |
| */ |
| public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for fixed64 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, long) |
| * ProtoOutputStream.write(long, long)} method. |
| */ |
| public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for fixed32 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, int) |
| * ProtoOutputStream.write(long, int)} method. |
| */ |
| |
| /** |
| * Field type code for fixed32 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, int) |
| * ProtoOutputStream.write(long, int)} method. |
| */ |
| public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for bool fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, boolean) |
| * ProtoOutputStream.write(long, boolean)} method. |
| */ |
| public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for string fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, String) |
| * ProtoOutputStream.write(long, String)} method. |
| */ |
| public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT; |
| |
| // public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated. |
| |
| /** |
| * Field type code for message fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#start(long) |
| * ProtoOutputStream.start(long)} method. |
| */ |
| public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for bytes fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, byte[]) |
| * ProtoOutputStream.write(long, byte[])} method. |
| */ |
| public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for uint32 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, int) |
| * ProtoOutputStream.write(long, int)} method. |
| */ |
| public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for enum fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, int) |
| * ProtoOutputStream.write(long, int)} method. |
| */ |
| public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for sfixed32 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, int) |
| * ProtoOutputStream.write(long, int)} method. |
| */ |
| public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for sfixed64 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, long) |
| * ProtoOutputStream.write(long, long)} method. |
| */ |
| public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for sint32 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, int) |
| * ProtoOutputStream.write(long, int)} method. |
| */ |
| public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT; |
| |
| /** |
| * Field type code for sint64 fields. Used to build constants in generated |
| * code for use with the {@link ProtoOutputStream#write(long, long) |
| * ProtoOutputStream.write(long, long)} method. |
| */ |
| public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT; |
| |
| private static final @NonNull String[] FIELD_TYPE_NAMES = new String[]{ |
| "Double", |
| "Float", |
| "Int64", |
| "UInt64", |
| "Int32", |
| "Fixed64", |
| "Fixed32", |
| "Bool", |
| "String", |
| "Group", // This field is deprecated but reserved here for indexing. |
| "Message", |
| "Bytes", |
| "UInt32", |
| "Enum", |
| "SFixed32", |
| "SFixed64", |
| "SInt32", |
| "SInt64", |
| }; |
| |
| // |
| // FieldId flags for whether the field is single, repeated or packed. |
| // |
| /** |
| * Bit offset for building a field id to be used with a |
| * <code>{@link ProtoOutputStream}.write(...)</code>. |
| * |
| * @see #FIELD_COUNT_MASK |
| * @see #FIELD_COUNT_UNKNOWN |
| * @see #FIELD_COUNT_SINGLE |
| * @see #FIELD_COUNT_REPEATED |
| * @see #FIELD_COUNT_PACKED |
| */ |
| public static final int FIELD_COUNT_SHIFT = 40; |
| |
| /** |
| * Bit mask for selecting the field count when reading a field id that |
| * is used with a <code>{@link ProtoOutputStream}.write(...)</code> method. |
| * |
| * @see #FIELD_COUNT_SHIFT |
| * @see #FIELD_COUNT_MASK |
| * @see #FIELD_COUNT_UNKNOWN |
| * @see #FIELD_COUNT_SINGLE |
| * @see #FIELD_COUNT_REPEATED |
| * @see #FIELD_COUNT_PACKED |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT; |
| |
| /** |
| * Unknown field count, encoded into a field id used with a |
| * <code>{@link ProtoOutputStream}.write(...)</code> method. |
| * |
| * @see #FIELD_COUNT_SHIFT |
| * @see #FIELD_COUNT_MASK |
| * @see #FIELD_COUNT_SINGLE |
| * @see #FIELD_COUNT_REPEATED |
| * @see #FIELD_COUNT_PACKED |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final long FIELD_COUNT_UNKNOWN = 0; |
| |
| /** |
| * Single field count, encoded into a field id used with a |
| * <code>{@link ProtoOutputStream}.write(...)</code> method. |
| * |
| * @see #FIELD_COUNT_SHIFT |
| * @see #FIELD_COUNT_MASK |
| * @see #FIELD_COUNT_UNKNOWN |
| * @see #FIELD_COUNT_REPEATED |
| * @see #FIELD_COUNT_PACKED |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT; |
| |
| /** |
| * Repeated field count, encoded into a field id used with a |
| * <code>{@link ProtoOutputStream}.write(...)</code> method. |
| * |
| * @see #FIELD_COUNT_SHIFT |
| * @see #FIELD_COUNT_MASK |
| * @see #FIELD_COUNT_UNKNOWN |
| * @see #FIELD_COUNT_SINGLE |
| * @see #FIELD_COUNT_PACKED |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT; |
| |
| /** |
| * Repeated packed field count, encoded into a field id used with a |
| * <code>{@link ProtoOutputStream}.write(...)</code> method. |
| * |
| * @see #FIELD_COUNT_SHIFT |
| * @see #FIELD_COUNT_MASK |
| * @see #FIELD_COUNT_UNKNOWN |
| * @see #FIELD_COUNT_SINGLE |
| * @see #FIELD_COUNT_REPEATED |
| * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf |
| * Encoding</a> |
| */ |
| public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT; |
| |
| |
| /** |
| * Get the developer-usable name of a field type. |
| */ |
| public static @Nullable String getFieldTypeString(@FieldType long fieldType) { |
| int index = ((int) ((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1; |
| if (index >= 0 && index < FIELD_TYPE_NAMES.length) { |
| return FIELD_TYPE_NAMES[index]; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Get the developer-usable name of a field count. |
| */ |
| public static @Nullable String getFieldCountString(long fieldCount) { |
| if (fieldCount == FIELD_COUNT_SINGLE) { |
| return ""; |
| } else if (fieldCount == FIELD_COUNT_REPEATED) { |
| return "Repeated"; |
| } else if (fieldCount == FIELD_COUNT_PACKED) { |
| return "Packed"; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Get the developer-usable name of a wire type. |
| */ |
| public static @Nullable String getWireTypeString(@WireType int wireType) { |
| switch (wireType) { |
| case WIRE_TYPE_VARINT: |
| return "Varint"; |
| case WIRE_TYPE_FIXED64: |
| return "Fixed64"; |
| case WIRE_TYPE_LENGTH_DELIMITED: |
| return "Length Delimited"; |
| case WIRE_TYPE_START_GROUP: |
| return "Start Group"; |
| case WIRE_TYPE_END_GROUP: |
| return "End Group"; |
| case WIRE_TYPE_FIXED32: |
| return "Fixed32"; |
| default: |
| return null; |
| } |
| } |
| |
| /** |
| * Get a debug string for a fieldId. |
| */ |
| public static @NonNull String getFieldIdString(long fieldId) { |
| final long fieldCount = fieldId & FIELD_COUNT_MASK; |
| String countString = getFieldCountString(fieldCount); |
| if (countString == null) { |
| countString = "fieldCount=" + fieldCount; |
| } |
| if (countString.length() > 0) { |
| countString += " "; |
| } |
| |
| final long fieldType = fieldId & FIELD_TYPE_MASK; |
| String typeString = getFieldTypeString(fieldType); |
| if (typeString == null) { |
| typeString = "fieldType=" + fieldType; |
| } |
| |
| return countString + typeString + " tag=" + ((int) fieldId) |
| + " fieldId=0x" + Long.toHexString(fieldId); |
| } |
| |
| /** |
| * Combine a fieldId (the field keys in the proto file) and the field flags. |
| * Mostly useful for testing because the generated code contains the fieldId |
| * constants. |
| */ |
| public static long makeFieldId(int id, long fieldFlags) { |
| return fieldFlags | (((long) id) & 0x0ffffffffL); |
| } |
| |
| // |
| // Child objects |
| // |
| |
| /** |
| * Make a token. |
| * Bits 61-63 - tag size (So we can go backwards later if the object had not data) |
| * - 3 bits, max value 7, max value needed 5 |
| * Bit 60 - true if the object is repeated (lets us require endObject or endRepeatedObject) |
| * Bits 59-51 - depth (For error checking) |
| * - 9 bits, max value 512, when checking, value is masked (if we really |
| * are more than 512 levels deep) |
| * Bits 32-50 - objectId (For error checking) |
| * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap |
| * because of the overflow, and only the tokens are compared. |
| * Bits 0-31 - offset of interest for the object. |
| */ |
| public static long makeToken(int tagSize, boolean repeated, int depth, int objectId, |
| int offset) { |
| return ((0x07L & (long) tagSize) << 61) |
| | (repeated ? (1L << 60) : 0) |
| | (0x01ffL & (long) depth) << 51 |
| | (0x07ffffL & (long) objectId) << 32 |
| | (0x0ffffffffL & (long) offset); |
| } |
| |
| /** |
| * Get the encoded tag size from the token. |
| * |
| * @hide |
| */ |
| public static int getTagSizeFromToken(long token) { |
| return (int) (0x7 & (token >> 61)); |
| } |
| |
| /** |
| * Get whether the token has the repeated bit set to true or false |
| * |
| * @hide |
| */ |
| public static boolean getRepeatedFromToken(long token) { |
| return (0x1 & (token >> 60)) != 0; |
| } |
| |
| /** |
| * Get the nesting depth from the token. |
| * |
| * @hide |
| */ |
| public static int getDepthFromToken(long token) { |
| return (int) (0x01ff & (token >> 51)); |
| } |
| |
| /** |
| * Get the object ID from the token. |
| * |
| * <p>The object ID is a serial number for the |
| * startObject calls that have happened on this object. The values are truncated |
| * to 9 bits, but that is sufficient for error checking. |
| * |
| * @hide |
| */ |
| public static int getObjectIdFromToken(long token) { |
| return (int) (0x07ffff & (token >> 32)); |
| } |
| |
| /** |
| * Get the location of the offset recorded in the token. |
| * |
| * @hide |
| */ |
| public static int getOffsetFromToken(long token) { |
| return (int) token; |
| } |
| |
| /** |
| * Convert the object ID to the ordinal value -- the n-th call to startObject. |
| * |
| * <p>The object IDs start at -1 and count backwards, so that the value is unlikely |
| * to alias with an actual size field that had been written. |
| * |
| * @hide |
| */ |
| public static int convertObjectIdToOrdinal(int objectId) { |
| return (-1 & 0x07ffff) - objectId; |
| } |
| |
| /** |
| * Return a debugging string of a token. |
| */ |
| public static @NonNull String token2String(long token) { |
| if (token == 0L) { |
| return "Token(0)"; |
| } else { |
| return "Token(val=0x" + Long.toHexString(token) |
| + " depth=" + getDepthFromToken(token) |
| + " object=" + convertObjectIdToOrdinal(getObjectIdFromToken(token)) |
| + " tagSize=" + getTagSizeFromToken(token) |
| + " offset=" + getOffsetFromToken(token) |
| + ')'; |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| protected ProtoStream() {} |
| } |