diff options
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.java | 261 |
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); + } +} |