aboutsummaryrefslogtreecommitdiff
path: root/java/core/src/main/java/com/google/protobuf/Proto2Manifest.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/core/src/main/java/com/google/protobuf/Proto2Manifest.java')
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto2Manifest.java261
1 files changed, 261 insertions, 0 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/Proto2Manifest.java b/java/core/src/main/java/com/google/protobuf/Proto2Manifest.java
new file mode 100644
index 00000000..b6eed904
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto2Manifest.java
@@ -0,0 +1,261 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/** Container for the field metadata of a single proto2 schema. */
+final class Proto2Manifest {
+ static final int INT_LENGTH = 4;
+ static final int LONG_LENGTH = INT_LENGTH * 2;
+ static final int LONGS_PER_FIELD = 2;
+ /**
+ * Note that field length is always a power of two so that we can use bit shifting (rather than
+ * division) to find the location of a field when parsing.
+ */
+ static final int FIELD_LENGTH = LONGS_PER_FIELD * LONG_LENGTH;
+
+ static final int FIELD_SHIFT = 4 /* 2^4 = 16 */;
+ static final int OFFSET_BITS = 20;
+ static final int OFFSET_MASK = 0XFFFFF;
+ static final long EMPTY_LONG = 0xFFFFFFFFFFFFFFFFL;
+
+ /**
+ * Holds all information for accessing the message fields. The layout is as follows (field
+ * positions are relative to the offset of the start of the field in the buffer):
+ *
+ * <p>
+ *
+ * <pre>
+ * [ 0 - 3] unused
+ * [ 4 - 31] field number
+ * [32 - 37] unused
+ * [38 - 43] field type
+ * [44 - 63] field offset
+ * [64 - 69] unused
+ * [70 - 75] field presence mask shift
+ * [76 - 95] presence field offset
+ * [96 - 127] unused
+ * </pre>
+ */
+ final ByteBuffer buffer;
+
+ final long address;
+ final long limit;
+ final int numFields;
+
+ final int minFieldNumber;
+ final int maxFieldNumber;
+
+ private Proto2Manifest(
+ ByteBuffer buffer,
+ long address,
+ long limit,
+ int numFields,
+ int minFieldNumber,
+ int maxFieldNumber) {
+ this.buffer = buffer;
+ this.address = address;
+ this.limit = limit;
+ this.numFields = numFields;
+ this.minFieldNumber = minFieldNumber;
+ this.maxFieldNumber = maxFieldNumber;
+ }
+
+ boolean isFieldInRange(int fieldNumber) {
+ return fieldNumber >= minFieldNumber && fieldNumber <= maxFieldNumber;
+ }
+
+ long tablePositionForFieldNumber(int fieldNumber) {
+ if (fieldNumber < minFieldNumber || fieldNumber > maxFieldNumber) {
+ return -1;
+ }
+
+ return indexToAddress(fieldNumber - minFieldNumber);
+ }
+
+ <T> boolean isFieldPresent(T message, long pos) {
+ int maskShiftAndOffset = UnsafeUtil.getInt(pos + LONG_LENGTH);
+ long offset = maskShiftAndOffset & OFFSET_MASK;
+ int mask = 1 << (maskShiftAndOffset >>> OFFSET_BITS);
+ return (UnsafeUtil.getInt(message, offset) & mask) != 0;
+ }
+
+ <T> void setFieldPresent(T message, long pos) {
+ int maskShiftAndOffset = UnsafeUtil.getInt(pos + LONG_LENGTH);
+ long offset = maskShiftAndOffset & OFFSET_MASK;
+ int mask = 1 << (maskShiftAndOffset >>> OFFSET_BITS);
+ UnsafeUtil.putInt(message, offset, UnsafeUtil.getInt(message, offset) | mask);
+ }
+
+ long lookupPositionForFieldNumber(int fieldNumber) {
+ int min = 0;
+ int max = numFields - 1;
+ while (min <= max) {
+ // Find the midpoint address.
+ int mid = (max + min) >>> 1;
+ long midAddress = indexToAddress(mid);
+ int midFieldNumber = numberAt(midAddress);
+ if (fieldNumber == midFieldNumber) {
+ // Found the field.
+ return midAddress;
+ }
+ if (fieldNumber < midFieldNumber) {
+ // Search the lower half.
+ max = mid - 1;
+ } else {
+ // Search the upper half.
+ min = mid + 1;
+ }
+ }
+ return -1;
+ }
+
+ int numberAt(long pos) {
+ return UnsafeUtil.getInt(pos);
+ }
+
+ int typeAndOffsetAt(long pos) {
+ return UnsafeUtil.getInt(pos + INT_LENGTH);
+ }
+
+ private long indexToAddress(int index) {
+ return address + (index << FIELD_SHIFT);
+ }
+
+ static byte type(int value) {
+ return (byte) (value >>> OFFSET_BITS);
+ }
+
+ static long offset(int value) {
+ return value & OFFSET_MASK;
+ }
+
+ static Proto2Manifest newTableManfiest(MessageInfo descriptor) {
+ List<FieldInfo> fds = descriptor.getFields();
+ if (fds.isEmpty()) {
+ throw new IllegalArgumentException("Table-based schema requires at least one field");
+ }
+
+ // Set up the buffer for direct indexing by field number.
+ final int minFieldNumber = fds.get(0).getFieldNumber();
+ final int maxFieldNumber = fds.get(fds.size() - 1).getFieldNumber();
+ final int numEntries = (maxFieldNumber - minFieldNumber) + 1;
+
+ int bufferLength = numEntries * FIELD_LENGTH;
+ ByteBuffer buffer = ByteBuffer.allocateDirect(bufferLength + LONG_LENGTH);
+ long tempAddress = UnsafeUtil.addressOffset(buffer);
+ if ((tempAddress & 7L) != 0) {
+ // Make sure that the memory address is 8-byte aligned.
+ tempAddress = (tempAddress & ~7L) + LONG_LENGTH;
+ }
+ final long address = tempAddress;
+ final long limit = address + bufferLength;
+
+ // Fill in the manifest data from the descriptors.
+ int fieldIndex = 0;
+ FieldInfo fd = fds.get(fieldIndex++);
+ for (int bufferIndex = 0; bufferIndex < bufferLength; bufferIndex += FIELD_LENGTH) {
+ final int fieldNumber = fd.getFieldNumber();
+ if (bufferIndex < ((fieldNumber - minFieldNumber) << FIELD_SHIFT)) {
+ // Mark this entry as "empty".
+ long skipLimit = address + bufferIndex + FIELD_LENGTH;
+ for (long skipPos = address + bufferIndex; skipPos < skipLimit; skipPos += LONG_LENGTH) {
+ UnsafeUtil.putLong(skipPos, EMPTY_LONG);
+ }
+ continue;
+ }
+
+ // We found the entry for the next field. Store the entry in the manifest for
+ // this field and increment the field index.
+ FieldType type = fd.getType();
+ long pos = address + bufferIndex;
+ UnsafeUtil.putInt(pos, fieldNumber);
+ UnsafeUtil.putInt(
+ pos + INT_LENGTH,
+ (type.id() << OFFSET_BITS) | (int) UnsafeUtil.objectFieldOffset(fd.getField()));
+ if (!type.isList()) {
+ int presenceOffset = (int) UnsafeUtil.objectFieldOffset(fd.getPresenceField());
+ int maskShift = Integer.numberOfTrailingZeros(fd.getPresenceMask());
+ UnsafeUtil.putInt(pos + LONG_LENGTH, maskShift << OFFSET_BITS | presenceOffset);
+ }
+
+ // Advance to the next field, unless we're at the end.
+ if (fieldIndex < fds.size()) {
+ fd = fds.get(fieldIndex++);
+ }
+ }
+
+ return new Proto2Manifest(buffer, address, limit, fds.size(), minFieldNumber, maxFieldNumber);
+ }
+
+ static Proto2Manifest newLookupManifest(MessageInfo descriptor) {
+ List<FieldInfo> fds = descriptor.getFields();
+
+ final int numFields = fds.size();
+ int bufferLength = numFields * FIELD_LENGTH;
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferLength + LONG_LENGTH);
+ long tempAddress = UnsafeUtil.addressOffset(buffer);
+ if ((tempAddress & 7L) != 0) {
+ // Make sure that the memory address is 8-byte aligned.
+ tempAddress = (tempAddress & ~7L) + LONG_LENGTH;
+ }
+ final long address = tempAddress;
+ final long limit = address + bufferLength;
+
+ // Allocate and populate the data buffer.
+ long pos = address;
+ for (int i = 0; i < fds.size(); ++i, pos += FIELD_LENGTH) {
+ FieldInfo fd = fds.get(i);
+ UnsafeUtil.putInt(pos, fd.getFieldNumber());
+ UnsafeUtil.putInt(
+ pos + INT_LENGTH,
+ (fd.getType().id() << OFFSET_BITS) | (int) UnsafeUtil.objectFieldOffset(fd.getField()));
+ if (!fd.getType().isList()) {
+ int presenceOffset = (int) UnsafeUtil.objectFieldOffset(fd.getPresenceField());
+ int maskShift = Integer.numberOfTrailingZeros(fd.getPresenceMask());
+ UnsafeUtil.putInt(pos + LONG_LENGTH, maskShift << OFFSET_BITS | presenceOffset);
+ }
+ }
+
+ if (numFields > 0) {
+ return new Proto2Manifest(
+ buffer,
+ address,
+ limit,
+ numFields,
+ fds.get(0).getFieldNumber(),
+ fds.get(numFields - 1).getFieldNumber());
+ }
+ return new Proto2Manifest(buffer, address, limit, numFields, -1, -1);
+ }
+}