| // 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 static com.google.protobuf.Internal.checkNotNull; |
| import static com.google.protobuf.WireFormat.FIXED32_SIZE; |
| import static com.google.protobuf.WireFormat.FIXED64_SIZE; |
| import static com.google.protobuf.WireFormat.MAX_VARINT32_SIZE; |
| import static com.google.protobuf.WireFormat.MAX_VARINT64_SIZE; |
| import static com.google.protobuf.WireFormat.MESSAGE_SET_ITEM; |
| import static com.google.protobuf.WireFormat.MESSAGE_SET_MESSAGE; |
| import static com.google.protobuf.WireFormat.MESSAGE_SET_TYPE_ID; |
| import static com.google.protobuf.WireFormat.WIRETYPE_END_GROUP; |
| import static com.google.protobuf.WireFormat.WIRETYPE_FIXED32; |
| import static com.google.protobuf.WireFormat.WIRETYPE_FIXED64; |
| import static com.google.protobuf.WireFormat.WIRETYPE_LENGTH_DELIMITED; |
| import static com.google.protobuf.WireFormat.WIRETYPE_START_GROUP; |
| import static com.google.protobuf.WireFormat.WIRETYPE_VARINT; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayDeque; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Queue; |
| |
| /** |
| * A protobuf writer that serializes messages in their binary form. Messages are serialized in |
| * reverse in order to avoid calculating the serialized size of each nested message. Since the |
| * message size is not known in advance, the writer employs a strategy of chunking and buffer |
| * chaining. Buffers are allocated as-needed by a provided {@link BufferAllocator}. Once writing is |
| * finished, the application can access the buffers in forward-writing order by calling {@link |
| * #complete()}. |
| * |
| * <p>Once {@link #complete()} has been called, the writer can not be reused for additional writes. |
| * The {@link #getTotalBytesWritten()} will continue to reflect the total of the write and will not |
| * be reset. |
| */ |
| @ExperimentalApi |
| abstract class BinaryWriter extends ByteOutput implements Writer { |
| public static final int DEFAULT_CHUNK_SIZE = 4096; |
| |
| private final BufferAllocator alloc; |
| private final int chunkSize; |
| |
| final ArrayDeque<AllocatedBuffer> buffers = new ArrayDeque<AllocatedBuffer>(4); |
| int totalDoneBytes; |
| |
| /** |
| * Creates a new {@link BinaryWriter} that will allocate heap buffers of {@link |
| * #DEFAULT_CHUNK_SIZE} as necessary. |
| */ |
| public static BinaryWriter newHeapInstance(BufferAllocator alloc) { |
| return newHeapInstance(alloc, DEFAULT_CHUNK_SIZE); |
| } |
| |
| /** |
| * Creates a new {@link BinaryWriter} that will allocate heap buffers of {@code chunkSize} as |
| * necessary. |
| */ |
| public static BinaryWriter newHeapInstance(BufferAllocator alloc, int chunkSize) { |
| return isUnsafeHeapSupported() |
| ? newUnsafeHeapInstance(alloc, chunkSize) |
| : newSafeHeapInstance(alloc, chunkSize); |
| } |
| |
| /** |
| * Creates a new {@link BinaryWriter} that will allocate direct (i.e. non-heap) buffers of {@link |
| * #DEFAULT_CHUNK_SIZE} as necessary. |
| */ |
| public static BinaryWriter newDirectInstance(BufferAllocator alloc) { |
| return newDirectInstance(alloc, DEFAULT_CHUNK_SIZE); |
| } |
| |
| /** |
| * Creates a new {@link BinaryWriter} that will allocate direct (i.e. non-heap) buffers of {@code |
| * chunkSize} as necessary. |
| */ |
| public static BinaryWriter newDirectInstance(BufferAllocator alloc, int chunkSize) { |
| return isUnsafeDirectSupported() |
| ? newUnsafeDirectInstance(alloc, chunkSize) |
| : newSafeDirectInstance(alloc, chunkSize); |
| } |
| |
| static boolean isUnsafeHeapSupported() { |
| return UnsafeHeapWriter.isSupported(); |
| } |
| |
| static boolean isUnsafeDirectSupported() { |
| return UnsafeDirectWriter.isSupported(); |
| } |
| |
| static BinaryWriter newSafeHeapInstance(BufferAllocator alloc, int chunkSize) { |
| return new SafeHeapWriter(alloc, chunkSize); |
| } |
| |
| static BinaryWriter newUnsafeHeapInstance(BufferAllocator alloc, int chunkSize) { |
| if (!isUnsafeHeapSupported()) { |
| throw new UnsupportedOperationException("Unsafe operations not supported"); |
| } |
| return new UnsafeHeapWriter(alloc, chunkSize); |
| } |
| |
| static BinaryWriter newSafeDirectInstance(BufferAllocator alloc, int chunkSize) { |
| return new SafeDirectWriter(alloc, chunkSize); |
| } |
| |
| static BinaryWriter newUnsafeDirectInstance(BufferAllocator alloc, int chunkSize) { |
| if (!isUnsafeDirectSupported()) { |
| throw new UnsupportedOperationException("Unsafe operations not supported"); |
| } |
| return new UnsafeDirectWriter(alloc, chunkSize); |
| } |
| |
| /** Only allow subclassing for inner classes. */ |
| private BinaryWriter(BufferAllocator alloc, int chunkSize) { |
| if (chunkSize <= 0) { |
| throw new IllegalArgumentException("chunkSize must be > 0"); |
| } |
| this.alloc = checkNotNull(alloc, "alloc"); |
| this.chunkSize = chunkSize; |
| } |
| |
| @Override |
| public final FieldOrder fieldOrder() { |
| return FieldOrder.DESCENDING; |
| } |
| |
| /** |
| * Completes the write operation and returns a queue of {@link AllocatedBuffer} objects in |
| * forward-writing order. This method should only be called once. |
| * |
| * <p>After calling this method, the writer can not be reused. Create a new writer for future |
| * writes. |
| */ |
| public final Queue<AllocatedBuffer> complete() { |
| finishCurrentBuffer(); |
| return buffers; |
| } |
| |
| @Override |
| public final void writeSFixed32(int fieldNumber, int value) throws IOException { |
| writeFixed32(fieldNumber, value); |
| } |
| |
| @Override |
| public final void writeInt64(int fieldNumber, long value) throws IOException { |
| writeUInt64(fieldNumber, value); |
| } |
| |
| @Override |
| public final void writeSFixed64(int fieldNumber, long value) throws IOException { |
| writeFixed64(fieldNumber, value); |
| } |
| |
| @Override |
| public final void writeFloat(int fieldNumber, float value) throws IOException { |
| writeFixed32(fieldNumber, Float.floatToRawIntBits(value)); |
| } |
| |
| @Override |
| public final void writeDouble(int fieldNumber, double value) throws IOException { |
| writeFixed64(fieldNumber, Double.doubleToRawLongBits(value)); |
| } |
| |
| @Override |
| public final void writeEnum(int fieldNumber, int value) throws IOException { |
| writeInt32(fieldNumber, value); |
| } |
| |
| @Override |
| public final void writeInt32List(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (list instanceof IntArrayList) { |
| writeInt32List_Internal(fieldNumber, (IntArrayList) list, packed); |
| } else { |
| writeInt32List_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeInt32List_Internal(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeInt32(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeInt32(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeInt32List_Internal(int fieldNumber, IntArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeInt32(list.getInt(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeInt32(fieldNumber, list.getInt(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeFixed32List(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (list instanceof IntArrayList) { |
| writeFixed32List_Internal(fieldNumber, (IntArrayList) list, packed); |
| } else { |
| writeFixed32List_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeFixed32List_Internal(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed32(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed32(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeFixed32List_Internal(int fieldNumber, IntArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed32(list.getInt(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed32(fieldNumber, list.getInt(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeInt64List(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| writeUInt64List(fieldNumber, list, packed); |
| } |
| |
| @Override |
| public final void writeUInt64List(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| if (list instanceof LongArrayList) { |
| writeUInt64List_Internal(fieldNumber, (LongArrayList) list, packed); |
| } else { |
| writeUInt64List_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeUInt64List_Internal(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeVarint64(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeUInt64(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeUInt64List_Internal(int fieldNumber, LongArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeVarint64(list.getLong(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeUInt64(fieldNumber, list.getLong(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeFixed64List(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| if (list instanceof LongArrayList) { |
| writeFixed64List_Internal(fieldNumber, (LongArrayList) list, packed); |
| } else { |
| writeFixed64List_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeFixed64List_Internal(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed64(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed64(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeFixed64List_Internal(int fieldNumber, LongArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed64(list.getLong(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed64(fieldNumber, list.getLong(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeFloatList(int fieldNumber, List<Float> list, boolean packed) |
| throws IOException { |
| if (list instanceof FloatArrayList) { |
| writeFloatList_Internal(fieldNumber, (FloatArrayList) list, packed); |
| } else { |
| writeFloatList_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeFloatList_Internal(int fieldNumber, List<Float> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed32(Float.floatToRawIntBits(list.get(i))); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFloat(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeFloatList_Internal(int fieldNumber, FloatArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed32(Float.floatToRawIntBits(list.getFloat(i))); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFloat(fieldNumber, list.getFloat(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeDoubleList(int fieldNumber, List<Double> list, boolean packed) |
| throws IOException { |
| if (list instanceof DoubleArrayList) { |
| writeDoubleList_Internal(fieldNumber, (DoubleArrayList) list, packed); |
| } else { |
| writeDoubleList_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeDoubleList_Internal(int fieldNumber, List<Double> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed64(Double.doubleToRawLongBits(list.get(i))); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeDouble(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeDoubleList_Internal(int fieldNumber, DoubleArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeFixed64(Double.doubleToRawLongBits(list.getDouble(i))); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeDouble(fieldNumber, list.getDouble(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeEnumList(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| writeInt32List(fieldNumber, list, packed); |
| } |
| |
| @Override |
| public final void writeBoolList(int fieldNumber, List<Boolean> list, boolean packed) |
| throws IOException { |
| if (list instanceof BooleanArrayList) { |
| writeBoolList_Internal(fieldNumber, (BooleanArrayList) list, packed); |
| } else { |
| writeBoolList_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeBoolList_Internal(int fieldNumber, List<Boolean> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + list.size()); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeBool(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeBool(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeBoolList_Internal(int fieldNumber, BooleanArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + list.size()); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeBool(list.getBoolean(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeBool(fieldNumber, list.getBoolean(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeStringList(int fieldNumber, List<String> list) throws IOException { |
| if (list instanceof LazyStringList) { |
| final LazyStringList lazyList = (LazyStringList) list; |
| for (int i = list.size() - 1; i >= 0; i--) { |
| writeLazyString(fieldNumber, lazyList.getRaw(i)); |
| } |
| } else { |
| for (int i = list.size() - 1; i >= 0; i--) { |
| writeString(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private void writeLazyString(int fieldNumber, Object value) throws IOException { |
| if (value instanceof String) { |
| writeString(fieldNumber, (String) value); |
| } else { |
| writeBytes(fieldNumber, (ByteString) value); |
| } |
| } |
| |
| @Override |
| public final void writeBytesList(int fieldNumber, List<ByteString> list) throws IOException { |
| for (int i = list.size() - 1; i >= 0; i--) { |
| writeBytes(fieldNumber, list.get(i)); |
| } |
| } |
| |
| @Override |
| public final void writeUInt32List(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (list instanceof IntArrayList) { |
| writeUInt32List_Internal(fieldNumber, (IntArrayList) list, packed); |
| } else { |
| writeUInt32List_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeUInt32List_Internal(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeVarint32(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeUInt32(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeUInt32List_Internal(int fieldNumber, IntArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeVarint32(list.getInt(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeUInt32(fieldNumber, list.getInt(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeSFixed32List(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| writeFixed32List(fieldNumber, list, packed); |
| } |
| |
| @Override |
| public final void writeSFixed64List(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| writeFixed64List(fieldNumber, list, packed); |
| } |
| |
| @Override |
| public final void writeSInt32List(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (list instanceof IntArrayList) { |
| writeSInt32List_Internal(fieldNumber, (IntArrayList) list, packed); |
| } else { |
| writeSInt32List_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private final void writeSInt32List_Internal(int fieldNumber, List<Integer> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt32(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt32(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeSInt32List_Internal(int fieldNumber, IntArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt32(list.getInt(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt32(fieldNumber, list.getInt(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeSInt64List(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| if (list instanceof LongArrayList) { |
| writeSInt64List_Internal(fieldNumber, (LongArrayList) list, packed); |
| } else { |
| writeSInt64List_Internal(fieldNumber, list, packed); |
| } |
| } |
| |
| private static final int MAP_KEY_NUMBER = 1; |
| private static final int MAP_VALUE_NUMBER = 2; |
| |
| @Override |
| public <K, V> void writeMap(int fieldNumber, MapEntryLite.Metadata<K, V> metadata, Map<K, V> map) |
| throws IOException { |
| // TODO(liujisi): Reverse write those entries. |
| for (Map.Entry<K, V> entry : map.entrySet()) { |
| int prevBytes = getTotalBytesWritten(); |
| writeMapEntryField(this, MAP_VALUE_NUMBER, metadata.valueType, entry.getValue()); |
| writeMapEntryField(this, MAP_KEY_NUMBER, metadata.keyType, entry.getKey()); |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| } |
| |
| static final void writeMapEntryField( |
| Writer writer, int fieldNumber, WireFormat.FieldType fieldType, Object object) |
| throws IOException { |
| switch (fieldType) { |
| case BOOL: |
| writer.writeBool(fieldNumber, (Boolean) object); |
| break; |
| case FIXED32: |
| writer.writeFixed32(fieldNumber, (Integer) object); |
| break; |
| case FIXED64: |
| writer.writeFixed64(fieldNumber, (Long) object); |
| break; |
| case INT32: |
| writer.writeInt32(fieldNumber, (Integer) object); |
| break; |
| case INT64: |
| writer.writeInt64(fieldNumber, (Long) object); |
| break; |
| case SFIXED32: |
| writer.writeSFixed32(fieldNumber, (Integer) object); |
| break; |
| case SFIXED64: |
| writer.writeSFixed64(fieldNumber, (Long) object); |
| break; |
| case SINT32: |
| writer.writeSInt32(fieldNumber, (Integer) object); |
| break; |
| case SINT64: |
| writer.writeSInt64(fieldNumber, (Long) object); |
| break; |
| case STRING: |
| writer.writeString(fieldNumber, (String) object); |
| break; |
| case UINT32: |
| writer.writeUInt32(fieldNumber, (Integer) object); |
| break; |
| case UINT64: |
| writer.writeUInt64(fieldNumber, (Long) object); |
| break; |
| case FLOAT: |
| writer.writeFloat(fieldNumber, (Float) object); |
| break; |
| case DOUBLE: |
| writer.writeDouble(fieldNumber, (Double) object); |
| break; |
| case MESSAGE: |
| writer.writeMessage(fieldNumber, object); |
| break; |
| case BYTES: |
| writer.writeBytes(fieldNumber, (ByteString) object); |
| break; |
| case ENUM: |
| if (object instanceof Internal.EnumLite) { |
| writer.writeEnum(fieldNumber, ((Internal.EnumLite) object).getNumber()); |
| } else if (object instanceof Integer) { |
| writer.writeEnum(fieldNumber, (Integer) object); |
| } else { |
| throw new IllegalArgumentException("Unexpected type for enum in map."); |
| } |
| break; |
| default: |
| throw new IllegalArgumentException("Unsupported map value type for: " + fieldType); |
| } |
| } |
| |
| private final void writeSInt64List_Internal(int fieldNumber, List<Long> list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt64(list.get(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt64(fieldNumber, list.get(i)); |
| } |
| } |
| } |
| |
| private final void writeSInt64List_Internal(int fieldNumber, LongArrayList list, boolean packed) |
| throws IOException { |
| if (packed) { |
| requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE)); |
| int prevBytes = getTotalBytesWritten(); |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt64(list.getLong(i)); |
| } |
| int length = getTotalBytesWritten() - prevBytes; |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } else { |
| for (int i = list.size() - 1; i >= 0; --i) { |
| writeSInt64(fieldNumber, list.getLong(i)); |
| } |
| } |
| } |
| |
| @Override |
| public final void writeMessageList(int fieldNumber, List<?> list) throws IOException { |
| for (int i = list.size() - 1; i >= 0; i--) { |
| writeMessage(fieldNumber, list.get(i)); |
| } |
| } |
| |
| @Override |
| public final void writeMessageList(int fieldNumber, List<?> list, Schema schema) |
| throws IOException { |
| for (int i = list.size() - 1; i >= 0; i--) { |
| writeMessage(fieldNumber, list.get(i), schema); |
| } |
| } |
| |
| @Override |
| public final void writeGroupList(int fieldNumber, List<?> list) throws IOException { |
| for (int i = list.size() - 1; i >= 0; i--) { |
| writeGroup(fieldNumber, list.get(i)); |
| } |
| } |
| |
| @Override |
| public final void writeGroupList(int fieldNumber, List<?> list, Schema schema) |
| throws IOException { |
| for (int i = list.size() - 1; i >= 0; i--) { |
| writeGroup(fieldNumber, list.get(i), schema); |
| } |
| } |
| |
| @Override |
| public final void writeMessageSetItem(int fieldNumber, Object value) throws IOException { |
| writeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP); |
| if (value instanceof ByteString) { |
| writeBytes(MESSAGE_SET_MESSAGE, (ByteString) value); |
| } else { |
| writeMessage(MESSAGE_SET_MESSAGE, value); |
| } |
| writeUInt32(MESSAGE_SET_TYPE_ID, fieldNumber); |
| writeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP); |
| } |
| |
| final AllocatedBuffer newHeapBuffer() { |
| return alloc.allocateHeapBuffer(chunkSize); |
| } |
| |
| final AllocatedBuffer newHeapBuffer(int capacity) { |
| return alloc.allocateHeapBuffer(Math.max(capacity, chunkSize)); |
| } |
| |
| final AllocatedBuffer newDirectBuffer() { |
| return alloc.allocateDirectBuffer(chunkSize); |
| } |
| |
| final AllocatedBuffer newDirectBuffer(int capacity) { |
| return alloc.allocateDirectBuffer(Math.max(capacity, chunkSize)); |
| } |
| |
| /** |
| * Gets the total number of bytes that have been written. This will not be reset by a call to |
| * {@link #complete()}. |
| */ |
| public abstract int getTotalBytesWritten(); |
| |
| abstract void requireSpace(int size); |
| |
| abstract void finishCurrentBuffer(); |
| |
| abstract void writeTag(int fieldNumber, int wireType); |
| |
| abstract void writeVarint32(int value); |
| |
| abstract void writeInt32(int value); |
| |
| abstract void writeSInt32(int value); |
| |
| abstract void writeFixed32(int value); |
| |
| abstract void writeVarint64(long value); |
| |
| abstract void writeSInt64(long value); |
| |
| abstract void writeFixed64(long value); |
| |
| abstract void writeBool(boolean value); |
| |
| abstract void writeString(String in); |
| |
| /** |
| * Not using the version in CodedOutputStream due to the fact that benchmarks have shown a |
| * performance improvement when returning a byte (rather than an int). |
| */ |
| private static byte computeUInt64SizeNoTag(long value) { |
| // handle two popular special cases up front ... |
| if ((value & (~0L << 7)) == 0L) { |
| // Byte 1 |
| return 1; |
| } |
| if (value < 0L) { |
| // Byte 10 |
| return 10; |
| } |
| // ... leaving us with 8 remaining, which we can divide and conquer |
| byte n = 2; |
| if ((value & (~0L << 35)) != 0L) { |
| // Byte 6-9 |
| n += 4; // + (value >>> 63); |
| value >>>= 28; |
| } |
| if ((value & (~0L << 21)) != 0L) { |
| // Byte 4-5 or 8-9 |
| n += 2; |
| value >>>= 14; |
| } |
| if ((value & (~0L << 14)) != 0L) { |
| // Byte 3 or 7 |
| n += 1; |
| } |
| return n; |
| } |
| |
| /** Writer that uses safe operations on target array. */ |
| private static final class SafeHeapWriter extends BinaryWriter { |
| private AllocatedBuffer allocatedBuffer; |
| private byte[] buffer; |
| private int offset; |
| private int limit; |
| private int offsetMinusOne; |
| private int limitMinusOne; |
| private int pos; |
| |
| SafeHeapWriter(BufferAllocator alloc, int chunkSize) { |
| super(alloc, chunkSize); |
| nextBuffer(); |
| } |
| |
| @Override |
| void finishCurrentBuffer() { |
| if (allocatedBuffer != null) { |
| totalDoneBytes += bytesWrittenToCurrentBuffer(); |
| allocatedBuffer.position((pos - allocatedBuffer.arrayOffset()) + 1); |
| allocatedBuffer = null; |
| pos = 0; |
| limitMinusOne = 0; |
| } |
| } |
| |
| private void nextBuffer() { |
| nextBuffer(newHeapBuffer()); |
| } |
| |
| private void nextBuffer(int capacity) { |
| nextBuffer(newHeapBuffer(capacity)); |
| } |
| |
| private void nextBuffer(AllocatedBuffer allocatedBuffer) { |
| if (!allocatedBuffer.hasArray()) { |
| throw new RuntimeException("Allocator returned non-heap buffer"); |
| } |
| |
| finishCurrentBuffer(); |
| |
| buffers.addFirst(allocatedBuffer); |
| |
| this.allocatedBuffer = allocatedBuffer; |
| this.buffer = allocatedBuffer.array(); |
| int arrayOffset = allocatedBuffer.arrayOffset(); |
| this.limit = arrayOffset + allocatedBuffer.limit(); |
| this.offset = arrayOffset + allocatedBuffer.position(); |
| this.offsetMinusOne = offset - 1; |
| this.limitMinusOne = limit - 1; |
| this.pos = limitMinusOne; |
| } |
| |
| @Override |
| public int getTotalBytesWritten() { |
| return totalDoneBytes + bytesWrittenToCurrentBuffer(); |
| } |
| |
| int bytesWrittenToCurrentBuffer() { |
| return limitMinusOne - pos; |
| } |
| |
| int spaceLeft() { |
| return pos - offsetMinusOne; |
| } |
| |
| @Override |
| public void writeUInt32(int fieldNumber, int value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeInt32(int fieldNumber, int value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt32(int fieldNumber, int value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeSInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed32(int fieldNumber, int value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE); |
| writeFixed32(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED32); |
| } |
| |
| @Override |
| public void writeUInt64(int fieldNumber, long value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeVarint64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt64(int fieldNumber, long value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeSInt64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed64(int fieldNumber, long value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE); |
| writeFixed64(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED64); |
| } |
| |
| @Override |
| public void writeBool(int fieldNumber, boolean value) throws IOException { |
| requireSpace(MAX_VARINT32_SIZE + 1); |
| write((byte) (value ? 1 : 0)); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeString(int fieldNumber, String value) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| writeString(value); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(2 * MAX_VARINT32_SIZE); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeBytes(int fieldNumber, ByteString value) throws IOException { |
| try { |
| value.writeToReverse(this); |
| } catch (IOException e) { |
| // Should never happen since the writer does not throw. |
| throw new RuntimeException(e); |
| } |
| |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value.size()); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| Protobuf.getInstance().writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value, Schema schema) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| schema.writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| Protobuf.getInstance().writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value, Schema schema) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| schema.writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeStartGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeEndGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| } |
| |
| @Override |
| void writeInt32(int value) { |
| if (value >= 0) { |
| writeVarint32(value); |
| } else { |
| writeVarint64(value); |
| } |
| } |
| |
| @Override |
| void writeSInt32(int value) { |
| writeVarint32(CodedOutputStream.encodeZigZag32(value)); |
| } |
| |
| @Override |
| void writeSInt64(long value) { |
| writeVarint64(CodedOutputStream.encodeZigZag64(value)); |
| } |
| |
| @Override |
| void writeBool(boolean value) { |
| write((byte) (value ? 1 : 0)); |
| } |
| |
| @Override |
| void writeTag(int fieldNumber, int wireType) { |
| writeVarint32(WireFormat.makeTag(fieldNumber, wireType)); |
| } |
| |
| @Override |
| void writeVarint32(int value) { |
| if ((value & (~0 << 7)) == 0) { |
| writeVarint32OneByte(value); |
| } else if ((value & (~0 << 14)) == 0) { |
| writeVarint32TwoBytes(value); |
| } else if ((value & (~0 << 21)) == 0) { |
| writeVarint32ThreeBytes(value); |
| } else if ((value & (~0 << 28)) == 0) { |
| writeVarint32FourBytes(value); |
| } else { |
| writeVarint32FiveBytes(value); |
| } |
| } |
| |
| private void writeVarint32OneByte(int value) { |
| buffer[pos--] = (byte) value; |
| } |
| |
| private void writeVarint32TwoBytes(int value) { |
| buffer[pos--] = (byte) (value >>> 7); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint32ThreeBytes(int value) { |
| buffer[pos--] = (byte) (value >>> 14); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint32FourBytes(int value) { |
| buffer[pos--] = (byte) (value >>> 21); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint32FiveBytes(int value) { |
| buffer[pos--] = (byte) (value >>> 28); |
| buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| @Override |
| void writeVarint64(long value) { |
| switch (computeUInt64SizeNoTag(value)) { |
| case 1: |
| writeVarint64OneByte(value); |
| break; |
| case 2: |
| writeVarint64TwoBytes(value); |
| break; |
| case 3: |
| writeVarint64ThreeBytes(value); |
| break; |
| case 4: |
| writeVarint64FourBytes(value); |
| break; |
| case 5: |
| writeVarint64FiveBytes(value); |
| break; |
| case 6: |
| writeVarint64SixBytes(value); |
| break; |
| case 7: |
| writeVarint64SevenBytes(value); |
| break; |
| case 8: |
| writeVarint64EightBytes(value); |
| break; |
| case 9: |
| writeVarint64NineBytes(value); |
| break; |
| case 10: |
| writeVarint64TenBytes(value); |
| break; |
| } |
| } |
| |
| private void writeVarint64OneByte(long value) { |
| buffer[pos--] = (byte) value; |
| } |
| |
| private void writeVarint64TwoBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 7); |
| buffer[pos--] = (byte) (((int) value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64ThreeBytes(long value) { |
| buffer[pos--] = (byte) (((int) value) >>> 14); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64FourBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 21); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64FiveBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 28); |
| buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64SixBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 35); |
| buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64SevenBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 42); |
| buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64EightBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 49); |
| buffer[pos--] = (byte) (((value >>> 42) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64NineBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 56); |
| buffer[pos--] = (byte) (((value >>> 49) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 42) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| private void writeVarint64TenBytes(long value) { |
| buffer[pos--] = (byte) (value >>> 63); |
| buffer[pos--] = (byte) (((value >>> 56) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 49) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 42) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80); |
| buffer[pos--] = (byte) ((value & 0x7F) | 0x80); |
| } |
| |
| @Override |
| void writeFixed32(int value) { |
| buffer[pos--] = (byte) ((value >> 24) & 0xFF); |
| buffer[pos--] = (byte) ((value >> 16) & 0xFF); |
| buffer[pos--] = (byte) ((value >> 8) & 0xFF); |
| buffer[pos--] = (byte) (value & 0xFF); |
| } |
| |
| @Override |
| void writeFixed64(long value) { |
| buffer[pos--] = (byte) ((int) (value >> 56) & 0xFF); |
| buffer[pos--] = (byte) ((int) (value >> 48) & 0xFF); |
| buffer[pos--] = (byte) ((int) (value >> 40) & 0xFF); |
| buffer[pos--] = (byte) ((int) (value >> 32) & 0xFF); |
| buffer[pos--] = (byte) ((int) (value >> 24) & 0xFF); |
| buffer[pos--] = (byte) ((int) (value >> 16) & 0xFF); |
| buffer[pos--] = (byte) ((int) (value >> 8) & 0xFF); |
| buffer[pos--] = (byte) ((int) (value) & 0xFF); |
| } |
| |
| @Override |
| void writeString(String in) { |
| // Request enough space to write the ASCII string. |
| requireSpace(in.length()); |
| |
| // We know the buffer is big enough... |
| int i = in.length() - 1; |
| // Set pos to the start of the ASCII string. |
| pos -= i; |
| // Designed to take advantage of |
| // https://wiki.openjdk.java.net/display/HotSpotInternals/RangeCheckElimination |
| for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) { |
| buffer[pos + i] = (byte) c; |
| } |
| if (i == -1) { |
| // Move pos past the String. |
| pos -= 1; |
| return; |
| } |
| pos += i; |
| for (char c; i >= 0; i--) { |
| c = in.charAt(i); |
| if (c < 0x80 && pos > offsetMinusOne) { |
| buffer[pos--] = (byte) c; |
| } else if (c < 0x800 && pos > offset) { // 11 bits, two UTF-8 bytes |
| buffer[pos--] = (byte) (0x80 | (0x3F & c)); |
| buffer[pos--] = (byte) ((0xF << 6) | (c >>> 6)); |
| } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) |
| && pos > (offset + 1)) { |
| // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes |
| buffer[pos--] = (byte) (0x80 | (0x3F & c)); |
| buffer[pos--] = (byte) (0x80 | (0x3F & (c >>> 6))); |
| buffer[pos--] = (byte) ((0xF << 5) | (c >>> 12)); |
| } else if (pos > (offset + 2)) { |
| // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, |
| // four UTF-8 bytes |
| char high = 0; |
| if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) { |
| throw new Utf8.UnpairedSurrogateException(i - 1, i); |
| } |
| i--; |
| int codePoint = Character.toCodePoint(high, c); |
| buffer[pos--] = (byte) (0x80 | (0x3F & codePoint)); |
| buffer[pos--] = (byte) (0x80 | (0x3F & (codePoint >>> 6))); |
| buffer[pos--] = (byte) (0x80 | (0x3F & (codePoint >>> 12))); |
| buffer[pos--] = (byte) ((0xF << 4) | (codePoint >>> 18)); |
| } else { |
| // Buffer is full - allocate a new one and revisit the current character. |
| requireSpace(i); |
| i++; |
| } |
| } |
| } |
| |
| @Override |
| public void write(byte value) { |
| buffer[pos--] = value; |
| } |
| |
| @Override |
| public void write(byte[] value, int offset, int length) { |
| if (spaceLeft() < length) { |
| nextBuffer(length); |
| } |
| |
| pos -= length; |
| System.arraycopy(value, offset, buffer, pos + 1, length); |
| } |
| |
| @Override |
| public void writeLazy(byte[] value, int offset, int length) { |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value, offset, length)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| return; |
| } |
| |
| pos -= length; |
| System.arraycopy(value, offset, buffer, pos + 1, length); |
| } |
| |
| @Override |
| public void write(ByteBuffer value) { |
| int length = value.remaining(); |
| if (spaceLeft() < length) { |
| nextBuffer(length); |
| } |
| |
| pos -= length; |
| value.get(buffer, pos + 1, length); |
| } |
| |
| @Override |
| public void writeLazy(ByteBuffer value) { |
| int length = value.remaining(); |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| } |
| |
| pos -= length; |
| value.get(buffer, pos + 1, length); |
| } |
| |
| @Override |
| void requireSpace(int size) { |
| if (spaceLeft() < size) { |
| nextBuffer(size); |
| } |
| } |
| } |
| |
| /** Writer that uses unsafe operations on a target array. */ |
| private static final class UnsafeHeapWriter extends BinaryWriter { |
| private AllocatedBuffer allocatedBuffer; |
| private byte[] buffer; |
| private long offset; |
| private long limit; |
| private long offsetMinusOne; |
| private long limitMinusOne; |
| private long pos; |
| |
| UnsafeHeapWriter(BufferAllocator alloc, int chunkSize) { |
| super(alloc, chunkSize); |
| nextBuffer(); |
| } |
| |
| /** Indicates whether the required unsafe operations are supported on this platform. */ |
| static boolean isSupported() { |
| return UnsafeUtil.hasUnsafeArrayOperations(); |
| } |
| |
| @Override |
| void finishCurrentBuffer() { |
| if (allocatedBuffer != null) { |
| totalDoneBytes += bytesWrittenToCurrentBuffer(); |
| allocatedBuffer.position((arrayPos() - allocatedBuffer.arrayOffset()) + 1); |
| allocatedBuffer = null; |
| pos = 0; |
| limitMinusOne = 0; |
| } |
| } |
| |
| private int arrayPos() { |
| return (int) pos; |
| } |
| |
| private void nextBuffer() { |
| nextBuffer(newHeapBuffer()); |
| } |
| |
| private void nextBuffer(int capacity) { |
| nextBuffer(newHeapBuffer(capacity)); |
| } |
| |
| private void nextBuffer(AllocatedBuffer allocatedBuffer) { |
| if (!allocatedBuffer.hasArray()) { |
| throw new RuntimeException("Allocator returned non-heap buffer"); |
| } |
| |
| finishCurrentBuffer(); |
| buffers.addFirst(allocatedBuffer); |
| |
| this.allocatedBuffer = allocatedBuffer; |
| this.buffer = allocatedBuffer.array(); |
| int arrayOffset = allocatedBuffer.arrayOffset(); |
| this.limit = arrayOffset + allocatedBuffer.limit(); |
| this.offset = arrayOffset + allocatedBuffer.position(); |
| this.offsetMinusOne = offset - 1; |
| this.limitMinusOne = limit - 1; |
| this.pos = limitMinusOne; |
| } |
| |
| @Override |
| public int getTotalBytesWritten() { |
| return totalDoneBytes + bytesWrittenToCurrentBuffer(); |
| } |
| |
| int bytesWrittenToCurrentBuffer() { |
| return (int) (limitMinusOne - pos); |
| } |
| |
| int spaceLeft() { |
| return (int) (pos - offsetMinusOne); |
| } |
| |
| @Override |
| public void writeUInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeSInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE); |
| writeFixed32(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED32); |
| } |
| |
| @Override |
| public void writeUInt64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeVarint64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeSInt64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE); |
| writeFixed64(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED64); |
| } |
| |
| @Override |
| public void writeBool(int fieldNumber, boolean value) { |
| requireSpace(MAX_VARINT32_SIZE + 1); |
| write((byte) (value ? 1 : 0)); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeString(int fieldNumber, String value) { |
| int prevBytes = getTotalBytesWritten(); |
| writeString(value); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(2 * MAX_VARINT32_SIZE); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeBytes(int fieldNumber, ByteString value) { |
| try { |
| value.writeToReverse(this); |
| } catch (IOException e) { |
| // Should never happen since the writer does not throw. |
| throw new RuntimeException(e); |
| } |
| |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value.size()); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| Protobuf.getInstance().writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value, Schema schema) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| schema.writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| Protobuf.getInstance().writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value, Schema schema) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| schema.writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeStartGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeEndGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| } |
| |
| @Override |
| void writeInt32(int value) { |
| if (value >= 0) { |
| writeVarint32(value); |
| } else { |
| writeVarint64(value); |
| } |
| } |
| |
| @Override |
| void writeSInt32(int value) { |
| writeVarint32(CodedOutputStream.encodeZigZag32(value)); |
| } |
| |
| @Override |
| void writeSInt64(long value) { |
| writeVarint64(CodedOutputStream.encodeZigZag64(value)); |
| } |
| |
| @Override |
| void writeBool(boolean value) { |
| write((byte) (value ? 1 : 0)); |
| } |
| |
| @Override |
| void writeTag(int fieldNumber, int wireType) { |
| writeVarint32(WireFormat.makeTag(fieldNumber, wireType)); |
| } |
| |
| @Override |
| void writeVarint32(int value) { |
| if ((value & (~0 << 7)) == 0) { |
| writeVarint32OneByte(value); |
| } else if ((value & (~0 << 14)) == 0) { |
| writeVarint32TwoBytes(value); |
| } else if ((value & (~0 << 21)) == 0) { |
| writeVarint32ThreeBytes(value); |
| } else if ((value & (~0 << 28)) == 0) { |
| writeVarint32FourBytes(value); |
| } else { |
| writeVarint32FiveBytes(value); |
| } |
| } |
| |
| private void writeVarint32OneByte(int value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) value); |
| } |
| |
| private void writeVarint32TwoBytes(int value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 7)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint32ThreeBytes(int value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 14)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint32FourBytes(int value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 21)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint32FiveBytes(int value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 28)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| @Override |
| void writeVarint64(long value) { |
| switch (computeUInt64SizeNoTag(value)) { |
| case 1: |
| writeVarint64OneByte(value); |
| break; |
| case 2: |
| writeVarint64TwoBytes(value); |
| break; |
| case 3: |
| writeVarint64ThreeBytes(value); |
| break; |
| case 4: |
| writeVarint64FourBytes(value); |
| break; |
| case 5: |
| writeVarint64FiveBytes(value); |
| break; |
| case 6: |
| writeVarint64SixBytes(value); |
| break; |
| case 7: |
| writeVarint64SevenBytes(value); |
| break; |
| case 8: |
| writeVarint64EightBytes(value); |
| break; |
| case 9: |
| writeVarint64NineBytes(value); |
| break; |
| case 10: |
| writeVarint64TenBytes(value); |
| break; |
| } |
| } |
| |
| private void writeVarint64OneByte(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) value); |
| } |
| |
| private void writeVarint64TwoBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 7)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((int) value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64ThreeBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((int) value) >>> 14)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64FourBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 21)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64FiveBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 28)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64SixBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 35)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64SevenBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 42)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64EightBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 49)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 42) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64NineBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 56)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 49) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 42) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64TenBytes(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 63)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 56) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 49) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 42) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| @Override |
| void writeFixed32(int value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value >> 24) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value >> 16) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((value >> 8) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (value & 0xFF)); |
| } |
| |
| @Override |
| void writeFixed64(long value) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 56) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 48) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 40) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 32) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 24) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 16) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 8) & 0xFF)); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value) & 0xFF)); |
| } |
| |
| @Override |
| void writeString(String in) { |
| // Request enough space to write the ASCII string. |
| requireSpace(in.length()); |
| |
| // We know the buffer is big enough... |
| int i = in.length() - 1; |
| // Set pos to the start of the ASCII string. |
| // pos -= i; |
| // Designed to take advantage of |
| // https://wiki.openjdk.java.net/display/HotSpotInternals/RangeCheckElimination |
| for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) c); |
| } |
| if (i == -1) { |
| // Move pos past the String. |
| return; |
| } |
| for (char c; i >= 0; i--) { |
| c = in.charAt(i); |
| if (c < 0x80 && pos > offsetMinusOne) { |
| UnsafeUtil.putByte(buffer, pos--, (byte) c); |
| } else if (c < 0x800 && pos > offset) { // 11 bits, two UTF-8 bytes |
| UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & c))); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((0xF << 6) | (c >>> 6))); |
| } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) |
| && pos > offset + 1) { |
| // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes |
| UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & c))); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & (c >>> 6)))); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((0xF << 5) | (c >>> 12))); |
| } else if (pos > offset + 2) { |
| // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, |
| // four UTF-8 bytes |
| final char high; |
| if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) { |
| throw new Utf8.UnpairedSurrogateException(i - 1, i); |
| } |
| i--; |
| int codePoint = Character.toCodePoint(high, c); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & codePoint))); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); |
| UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); |
| UnsafeUtil.putByte(buffer, pos--, (byte) ((0xF << 4) | (codePoint >>> 18))); |
| } else { |
| // Buffer is full - allocate a new one and revisit the current character. |
| requireSpace(i); |
| i++; |
| } |
| } |
| } |
| |
| @Override |
| public void write(byte value) { |
| UnsafeUtil.putByte(buffer, pos--, value); |
| } |
| |
| @Override |
| public void write(byte[] value, int offset, int length) { |
| if (offset < 0 || offset + length > value.length) { |
| throw new ArrayIndexOutOfBoundsException( |
| String.format("value.length=%d, offset=%d, length=%d", value.length, offset, length)); |
| } |
| requireSpace(length); |
| |
| pos -= length; |
| System.arraycopy(value, offset, buffer, arrayPos() + 1, length); |
| } |
| |
| @Override |
| public void writeLazy(byte[] value, int offset, int length) { |
| if (offset < 0 || offset + length > value.length) { |
| throw new ArrayIndexOutOfBoundsException( |
| String.format("value.length=%d, offset=%d, length=%d", value.length, offset, length)); |
| } |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value, offset, length)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| return; |
| } |
| |
| pos -= length; |
| System.arraycopy(value, offset, buffer, arrayPos() + 1, length); |
| } |
| |
| @Override |
| public void write(ByteBuffer value) { |
| int length = value.remaining(); |
| requireSpace(length); |
| |
| pos -= length; |
| value.get(buffer, arrayPos() + 1, length); |
| } |
| |
| @Override |
| public void writeLazy(ByteBuffer value) { |
| int length = value.remaining(); |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| } |
| |
| pos -= length; |
| value.get(buffer, arrayPos() + 1, length); |
| } |
| |
| @Override |
| void requireSpace(int size) { |
| if (spaceLeft() < size) { |
| nextBuffer(size); |
| } |
| } |
| } |
| |
| /** Writer that uses safe operations on a target {@link ByteBuffer}. */ |
| private static final class SafeDirectWriter extends BinaryWriter { |
| private ByteBuffer buffer; |
| private int limitMinusOne; |
| private int pos; |
| |
| SafeDirectWriter(BufferAllocator alloc, int chunkSize) { |
| super(alloc, chunkSize); |
| nextBuffer(); |
| } |
| |
| private void nextBuffer() { |
| nextBuffer(newDirectBuffer()); |
| } |
| |
| private void nextBuffer(int capacity) { |
| nextBuffer(newDirectBuffer(capacity)); |
| } |
| |
| private void nextBuffer(AllocatedBuffer allocatedBuffer) { |
| if (!allocatedBuffer.hasNioBuffer()) { |
| throw new RuntimeException("Allocated buffer does not have NIO buffer"); |
| } |
| ByteBuffer nioBuffer = allocatedBuffer.nioBuffer(); |
| if (!nioBuffer.isDirect()) { |
| throw new RuntimeException("Allocator returned non-direct buffer"); |
| } |
| |
| finishCurrentBuffer(); |
| buffers.addFirst(allocatedBuffer); |
| |
| buffer = nioBuffer; |
| buffer.limit(buffer.capacity()); |
| buffer.position(0); |
| // Set byte order to little endian for fast writing of fixed 32/64. |
| buffer.order(ByteOrder.LITTLE_ENDIAN); |
| |
| limitMinusOne = buffer.limit() - 1; |
| pos = limitMinusOne; |
| } |
| |
| @Override |
| public int getTotalBytesWritten() { |
| return totalDoneBytes + bytesWrittenToCurrentBuffer(); |
| } |
| |
| private int bytesWrittenToCurrentBuffer() { |
| return limitMinusOne - pos; |
| } |
| |
| private int spaceLeft() { |
| return pos + 1; |
| } |
| |
| @Override |
| void finishCurrentBuffer() { |
| if (buffer != null) { |
| totalDoneBytes += bytesWrittenToCurrentBuffer(); |
| // Update the indices on the netty buffer. |
| buffer.position(pos + 1); |
| buffer = null; |
| pos = 0; |
| limitMinusOne = 0; |
| } |
| } |
| |
| @Override |
| public void writeUInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeSInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE); |
| writeFixed32(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED32); |
| } |
| |
| @Override |
| public void writeUInt64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeVarint64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeSInt64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE); |
| writeFixed64(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED64); |
| } |
| |
| @Override |
| public void writeBool(int fieldNumber, boolean value) { |
| requireSpace(MAX_VARINT32_SIZE + 1); |
| write((byte) (value ? 1 : 0)); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeString(int fieldNumber, String value) { |
| int prevBytes = getTotalBytesWritten(); |
| writeString(value); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(2 * MAX_VARINT32_SIZE); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeBytes(int fieldNumber, ByteString value) { |
| try { |
| value.writeToReverse(this); |
| } catch (IOException e) { |
| // Should never happen since the writer does not throw. |
| throw new RuntimeException(e); |
| } |
| |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value.size()); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| Protobuf.getInstance().writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value, Schema schema) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| schema.writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| Protobuf.getInstance().writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value, Schema schema) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| schema.writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeStartGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeEndGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| } |
| |
| @Override |
| void writeInt32(int value) { |
| if (value >= 0) { |
| writeVarint32(value); |
| } else { |
| writeVarint64(value); |
| } |
| } |
| |
| @Override |
| void writeSInt32(int value) { |
| writeVarint32(CodedOutputStream.encodeZigZag32(value)); |
| } |
| |
| @Override |
| void writeSInt64(long value) { |
| writeVarint64(CodedOutputStream.encodeZigZag64(value)); |
| } |
| |
| @Override |
| void writeBool(boolean value) { |
| write((byte) (value ? 1 : 0)); |
| } |
| |
| @Override |
| void writeTag(int fieldNumber, int wireType) { |
| writeVarint32(WireFormat.makeTag(fieldNumber, wireType)); |
| } |
| |
| @Override |
| void writeVarint32(int value) { |
| if ((value & (~0 << 7)) == 0) { |
| writeVarint32OneByte(value); |
| } else if ((value & (~0 << 14)) == 0) { |
| writeVarint32TwoBytes(value); |
| } else if ((value & (~0 << 21)) == 0) { |
| writeVarint32ThreeBytes(value); |
| } else if ((value & (~0 << 28)) == 0) { |
| writeVarint32FourBytes(value); |
| } else { |
| writeVarint32FiveBytes(value); |
| } |
| } |
| |
| private void writeVarint32OneByte(int value) { |
| buffer.put(pos--, (byte) value); |
| } |
| |
| private void writeVarint32TwoBytes(int value) { |
| // Byte order is little-endian. |
| pos -= 2; |
| buffer.putShort(pos + 1, (short) (((value & (0x7F << 7)) << 1) | ((value & 0x7F) | 0x80))); |
| } |
| |
| private void writeVarint32ThreeBytes(int value) { |
| // Byte order is little-endian. |
| pos -= 3; |
| buffer.putInt( |
| pos, |
| ((value & (0x7F << 14)) << 10) |
| | (((value & (0x7F << 7)) | (0x80 << 7)) << 9) |
| | ((value & 0x7F) | 0x80) << 8); |
| } |
| |
| private void writeVarint32FourBytes(int value) { |
| // Byte order is little-endian. |
| pos -= 4; |
| buffer.putInt( |
| pos + 1, |
| ((value & (0x7F << 21)) << 3) |
| | (((value & (0x7F << 14)) | (0x80 << 14)) << 2) |
| | (((value & (0x7F << 7)) | (0x80 << 7)) << 1) |
| | ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint32FiveBytes(int value) { |
| // Byte order is little-endian. |
| buffer.put(pos--, (byte) (value >>> 28)); |
| pos -= 4; |
| buffer.putInt( |
| pos + 1, |
| ((((value >>> 21) & 0x7F) | 0x80) << 24) |
| | ((((value >>> 14) & 0x7F) | 0x80) << 16) |
| | ((((value >>> 7) & 0x7F) | 0x80) << 8) |
| | ((value & 0x7F) | 0x80)); |
| } |
| |
| @Override |
| void writeVarint64(long value) { |
| switch (computeUInt64SizeNoTag(value)) { |
| case 1: |
| writeVarint64OneByte(value); |
| break; |
| case 2: |
| writeVarint64TwoBytes(value); |
| break; |
| case 3: |
| writeVarint64ThreeBytes(value); |
| break; |
| case 4: |
| writeVarint64FourBytes(value); |
| break; |
| case 5: |
| writeVarint64FiveBytes(value); |
| break; |
| case 6: |
| writeVarint64SixBytes(value); |
| break; |
| case 7: |
| writeVarint64SevenBytes(value); |
| break; |
| case 8: |
| writeVarint64EightBytes(value); |
| break; |
| case 9: |
| writeVarint64NineBytes(value); |
| break; |
| case 10: |
| writeVarint64TenBytes(value); |
| break; |
| } |
| } |
| |
| private void writeVarint64OneByte(long value) { |
| writeVarint32OneByte((int) value); |
| } |
| |
| private void writeVarint64TwoBytes(long value) { |
| writeVarint32TwoBytes((int) value); |
| } |
| |
| private void writeVarint64ThreeBytes(long value) { |
| writeVarint32ThreeBytes((int) value); |
| } |
| |
| private void writeVarint64FourBytes(long value) { |
| writeVarint32FourBytes((int) value); |
| } |
| |
| private void writeVarint64FiveBytes(long value) { |
| // Byte order is little-endian. |
| pos -= 5; |
| buffer.putLong( |
| pos - 2, |
| ((value & (0x7FL << 28)) << 28) |
| | (((value & (0x7F << 21)) | (0x80 << 21)) << 27) |
| | (((value & (0x7F << 14)) | (0x80 << 14)) << 26) |
| | (((value & (0x7F << 7)) | (0x80 << 7)) << 25) |
| | (((value & 0x7F) | 0x80)) << 24); |
| } |
| |
| private void writeVarint64SixBytes(long value) { |
| // Byte order is little-endian. |
| pos -= 6; |
| buffer.putLong( |
| pos - 1, |
| ((value & (0x7FL << 35)) << 21) |
| | (((value & (0x7FL << 28)) | (0x80L << 28)) << 20) |
| | (((value & (0x7F << 21)) | (0x80 << 21)) << 19) |
| | (((value & (0x7F << 14)) | (0x80 << 14)) << 18) |
| | (((value & (0x7F << 7)) | (0x80 << 7)) << 17) |
| | (((value & 0x7F) | 0x80)) << 16); |
| } |
| |
| private void writeVarint64SevenBytes(long value) { |
| // Byte order is little-endian. |
| pos -= 7; |
| buffer.putLong( |
| pos, |
| ((value & (0x7FL << 42)) << 14) |
| | (((value & (0x7FL << 35)) | (0x80L << 35)) << 13) |
| | (((value & (0x7FL << 28)) | (0x80L << 28)) << 12) |
| | (((value & (0x7F << 21)) | (0x80 << 21)) << 11) |
| | (((value & (0x7F << 14)) | (0x80 << 14)) << 10) |
| | (((value & (0x7F << 7)) | (0x80 << 7)) << 9) |
| | (((value & 0x7F) | 0x80)) << 8); |
| } |
| |
| private void writeVarint64EightBytes(long value) { |
| // Byte order is little-endian. |
| pos -= 8; |
| buffer.putLong( |
| pos + 1, |
| ((value & (0x7FL << 49)) << 7) |
| | (((value & (0x7FL << 42)) | (0x80L << 42)) << 6) |
| | (((value & (0x7FL << 35)) | (0x80L << 35)) << 5) |
| | (((value & (0x7FL << 28)) | (0x80L << 28)) << 4) |
| | (((value & (0x7F << 21)) | (0x80 << 21)) << 3) |
| | (((value & (0x7F << 14)) | (0x80 << 14)) << 2) |
| | (((value & (0x7F << 7)) | (0x80 << 7)) << 1) |
| | ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64EightBytesWithSign(long value) { |
| // Byte order is little-endian. |
| pos -= 8; |
| buffer.putLong( |
| pos + 1, |
| (((value & (0x7FL << 49)) | (0x80L << 49)) << 7) |
| | (((value & (0x7FL << 42)) | (0x80L << 42)) << 6) |
| | (((value & (0x7FL << 35)) | (0x80L << 35)) << 5) |
| | (((value & (0x7FL << 28)) | (0x80L << 28)) << 4) |
| | (((value & (0x7F << 21)) | (0x80 << 21)) << 3) |
| | (((value & (0x7F << 14)) | (0x80 << 14)) << 2) |
| | (((value & (0x7F << 7)) | (0x80 << 7)) << 1) |
| | ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64NineBytes(long value) { |
| buffer.put(pos--, (byte) (value >>> 56)); |
| writeVarint64EightBytesWithSign(value & 0xFFFFFFFFFFFFFFL); |
| } |
| |
| private void writeVarint64TenBytes(long value) { |
| buffer.put(pos--, (byte) (value >>> 63)); |
| buffer.put(pos--, (byte) (((value >>> 56) & 0x7F) | 0x80)); |
| writeVarint64EightBytesWithSign(value & 0xFFFFFFFFFFFFFFL); |
| } |
| |
| @Override |
| void writeFixed32(int value) { |
| pos -= 4; |
| buffer.putInt(pos + 1, value); |
| } |
| |
| @Override |
| void writeFixed64(long value) { |
| pos -= 8; |
| buffer.putLong(pos + 1, value); |
| } |
| |
| @Override |
| void writeString(String in) { |
| // Request enough space to write the ASCII string. |
| requireSpace(in.length()); |
| |
| // We know the buffer is big enough... |
| int i = in.length() - 1; |
| pos -= i; |
| // Designed to take advantage of |
| // https://wiki.openjdk.java.net/display/HotSpotInternals/RangeCheckElimination |
| for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) { |
| buffer.put(pos + i, (byte) c); |
| } |
| if (i == -1) { |
| // Move the position past the ASCII string. |
| pos -= 1; |
| return; |
| } |
| pos += i; |
| for (char c; i >= 0; i--) { |
| c = in.charAt(i); |
| if (c < 0x80 && pos >= 0) { |
| buffer.put(pos--, (byte) c); |
| } else if (c < 0x800 && pos > 0) { // 11 bits, two UTF-8 bytes |
| buffer.put(pos--, (byte) (0x80 | (0x3F & c))); |
| buffer.put(pos--, (byte) ((0xF << 6) | (c >>> 6))); |
| } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && pos > 1) { |
| // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes |
| buffer.put(pos--, (byte) (0x80 | (0x3F & c))); |
| buffer.put(pos--, (byte) (0x80 | (0x3F & (c >>> 6)))); |
| buffer.put(pos--, (byte) ((0xF << 5) | (c >>> 12))); |
| } else if (pos > 2) { |
| // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, |
| // four UTF-8 bytes |
| char high = 0; |
| if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) { |
| throw new Utf8.UnpairedSurrogateException(i - 1, i); |
| } |
| i--; |
| int codePoint = Character.toCodePoint(high, c); |
| buffer.put(pos--, (byte) (0x80 | (0x3F & codePoint))); |
| buffer.put(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); |
| buffer.put(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); |
| buffer.put(pos--, (byte) ((0xF << 4) | (codePoint >>> 18))); |
| } else { |
| // Buffer is full - allocate a new one and revisit the current character. |
| requireSpace(i); |
| i++; |
| } |
| } |
| } |
| |
| @Override |
| public void write(byte value) { |
| buffer.put(pos--, value); |
| } |
| |
| @Override |
| public void write(byte[] value, int offset, int length) { |
| if (spaceLeft() < length) { |
| nextBuffer(length); |
| } |
| |
| pos -= length; |
| buffer.position(pos + 1); |
| buffer.put(value, offset, length); |
| } |
| |
| @Override |
| public void writeLazy(byte[] value, int offset, int length) { |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value, offset, length)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| return; |
| } |
| |
| pos -= length; |
| buffer.position(pos + 1); |
| buffer.put(value, offset, length); |
| } |
| |
| @Override |
| public void write(ByteBuffer value) { |
| int length = value.remaining(); |
| if (spaceLeft() < length) { |
| nextBuffer(length); |
| } |
| |
| pos -= length; |
| buffer.position(pos + 1); |
| buffer.put(value); |
| } |
| |
| @Override |
| public void writeLazy(ByteBuffer value) { |
| int length = value.remaining(); |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| return; |
| } |
| |
| pos -= length; |
| buffer.position(pos + 1); |
| buffer.put(value); |
| } |
| |
| @Override |
| void requireSpace(int size) { |
| if (spaceLeft() < size) { |
| nextBuffer(size); |
| } |
| } |
| } |
| |
| /** Writer that uses unsafe operations on a target {@link ByteBuffer}. */ |
| private static final class UnsafeDirectWriter extends BinaryWriter { |
| private ByteBuffer buffer; |
| private long bufferOffset; |
| private long limitMinusOne; |
| private long pos; |
| |
| UnsafeDirectWriter(BufferAllocator alloc, int chunkSize) { |
| super(alloc, chunkSize); |
| nextBuffer(); |
| } |
| |
| /** Indicates whether the required unsafe operations are supported on this platform. */ |
| private static boolean isSupported() { |
| return UnsafeUtil.hasUnsafeByteBufferOperations(); |
| } |
| |
| private void nextBuffer() { |
| nextBuffer(newDirectBuffer()); |
| } |
| |
| private void nextBuffer(int capacity) { |
| nextBuffer(newDirectBuffer(capacity)); |
| } |
| |
| private void nextBuffer(AllocatedBuffer allocatedBuffer) { |
| if (!allocatedBuffer.hasNioBuffer()) { |
| throw new RuntimeException("Allocated buffer does not have NIO buffer"); |
| } |
| ByteBuffer nioBuffer = allocatedBuffer.nioBuffer(); |
| if (!nioBuffer.isDirect()) { |
| throw new RuntimeException("Allocator returned non-direct buffer"); |
| } |
| |
| finishCurrentBuffer(); |
| buffers.addFirst(allocatedBuffer); |
| |
| buffer = nioBuffer; |
| buffer.limit(buffer.capacity()); |
| buffer.position(0); |
| |
| bufferOffset = UnsafeUtil.addressOffset(buffer); |
| limitMinusOne = bufferOffset + (buffer.limit() - 1); |
| pos = limitMinusOne; |
| } |
| |
| @Override |
| public int getTotalBytesWritten() { |
| return totalDoneBytes + bytesWrittenToCurrentBuffer(); |
| } |
| |
| private int bytesWrittenToCurrentBuffer() { |
| return (int) (limitMinusOne - pos); |
| } |
| |
| private int spaceLeft() { |
| return bufferPos() + 1; |
| } |
| |
| @Override |
| void finishCurrentBuffer() { |
| if (buffer != null) { |
| totalDoneBytes += bytesWrittenToCurrentBuffer(); |
| // Update the indices on the netty buffer. |
| buffer.position(bufferPos() + 1); |
| buffer = null; |
| pos = 0; |
| limitMinusOne = 0; |
| } |
| } |
| |
| private int bufferPos() { |
| return (int) (pos - bufferOffset); |
| } |
| |
| @Override |
| public void writeUInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeSInt32(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed32(int fieldNumber, int value) { |
| requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE); |
| writeFixed32(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED32); |
| } |
| |
| @Override |
| public void writeUInt64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeVarint64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeSInt64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE); |
| writeSInt64(value); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeFixed64(int fieldNumber, long value) { |
| requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE); |
| writeFixed64(value); |
| writeTag(fieldNumber, WIRETYPE_FIXED64); |
| } |
| |
| @Override |
| public void writeBool(int fieldNumber, boolean value) { |
| requireSpace(MAX_VARINT32_SIZE + 1); |
| write((byte) (value ? 1 : 0)); |
| writeTag(fieldNumber, WIRETYPE_VARINT); |
| } |
| |
| @Override |
| public void writeString(int fieldNumber, String value) { |
| int prevBytes = getTotalBytesWritten(); |
| writeString(value); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(2 * MAX_VARINT32_SIZE); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeBytes(int fieldNumber, ByteString value) { |
| try { |
| value.writeToReverse(this); |
| } catch (IOException e) { |
| // Should never happen since the writer does not throw. |
| throw new RuntimeException(e); |
| } |
| |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(value.size()); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| Protobuf.getInstance().writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeMessage(int fieldNumber, Object value, Schema schema) throws IOException { |
| int prevBytes = getTotalBytesWritten(); |
| schema.writeTo(value, this); |
| int length = getTotalBytesWritten() - prevBytes; |
| requireSpace(MAX_VARINT32_SIZE * 2); |
| writeVarint32(length); |
| writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| Protobuf.getInstance().writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeGroup(int fieldNumber, Object value, Schema schema) throws IOException { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| schema.writeTo(value, this); |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeStartGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_START_GROUP); |
| } |
| |
| @Override |
| public void writeEndGroup(int fieldNumber) { |
| writeTag(fieldNumber, WIRETYPE_END_GROUP); |
| } |
| |
| @Override |
| void writeInt32(int value) { |
| if (value >= 0) { |
| writeVarint32(value); |
| } else { |
| writeVarint64(value); |
| } |
| } |
| |
| @Override |
| void writeSInt32(int value) { |
| writeVarint32(CodedOutputStream.encodeZigZag32(value)); |
| } |
| |
| @Override |
| void writeSInt64(long value) { |
| writeVarint64(CodedOutputStream.encodeZigZag64(value)); |
| } |
| |
| @Override |
| void writeBool(boolean value) { |
| write((byte) (value ? 1 : 0)); |
| } |
| |
| @Override |
| void writeTag(int fieldNumber, int wireType) { |
| writeVarint32(WireFormat.makeTag(fieldNumber, wireType)); |
| } |
| |
| @Override |
| void writeVarint32(int value) { |
| if ((value & (~0 << 7)) == 0) { |
| writeVarint32OneByte(value); |
| } else if ((value & (~0 << 14)) == 0) { |
| writeVarint32TwoBytes(value); |
| } else if ((value & (~0 << 21)) == 0) { |
| writeVarint32ThreeBytes(value); |
| } else if ((value & (~0 << 28)) == 0) { |
| writeVarint32FourBytes(value); |
| } else { |
| writeVarint32FiveBytes(value); |
| } |
| } |
| |
| private void writeVarint32OneByte(int value) { |
| UnsafeUtil.putByte(pos--, (byte) value); |
| } |
| |
| private void writeVarint32TwoBytes(int value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 7)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint32ThreeBytes(int value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 14)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint32FourBytes(int value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 21)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint32FiveBytes(int value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 28)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| @Override |
| void writeVarint64(long value) { |
| switch (computeUInt64SizeNoTag(value)) { |
| case 1: |
| writeVarint64OneByte(value); |
| break; |
| case 2: |
| writeVarint64TwoBytes(value); |
| break; |
| case 3: |
| writeVarint64ThreeBytes(value); |
| break; |
| case 4: |
| writeVarint64FourBytes(value); |
| break; |
| case 5: |
| writeVarint64FiveBytes(value); |
| break; |
| case 6: |
| writeVarint64SixBytes(value); |
| break; |
| case 7: |
| writeVarint64SevenBytes(value); |
| break; |
| case 8: |
| writeVarint64EightBytes(value); |
| break; |
| case 9: |
| writeVarint64NineBytes(value); |
| break; |
| case 10: |
| writeVarint64TenBytes(value); |
| break; |
| } |
| } |
| |
| private void writeVarint64OneByte(long value) { |
| UnsafeUtil.putByte(pos--, (byte) value); |
| } |
| |
| private void writeVarint64TwoBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 7)); |
| UnsafeUtil.putByte(pos--, (byte) (((int) value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64ThreeBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (((int) value) >>> 14)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64FourBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 21)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64FiveBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 28)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64SixBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 35)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64SevenBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 42)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64EightBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 49)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 42) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64NineBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 56)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 49) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 42) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| private void writeVarint64TenBytes(long value) { |
| UnsafeUtil.putByte(pos--, (byte) (value >>> 63)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 56) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 49) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 42) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80)); |
| UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80)); |
| } |
| |
| @Override |
| void writeFixed32(int value) { |
| UnsafeUtil.putByte(pos--, (byte) ((value >> 24) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((value >> 16) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((value >> 8) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) (value & 0xFF)); |
| } |
| |
| @Override |
| void writeFixed64(long value) { |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 56) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 48) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 40) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 32) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 24) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 16) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 8) & 0xFF)); |
| UnsafeUtil.putByte(pos--, (byte) ((int) (value) & 0xFF)); |
| } |
| |
| @Override |
| void writeString(String in) { |
| // Request enough space to write the ASCII string. |
| requireSpace(in.length()); |
| |
| // We know the buffer is big enough... |
| int i = in.length() - 1; |
| // Designed to take advantage of |
| // https://wiki.openjdk.java.net/display/HotSpotInternals/RangeCheckElimination |
| for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) { |
| UnsafeUtil.putByte(pos--, (byte) c); |
| } |
| if (i == -1) { |
| // ASCII. |
| return; |
| } |
| for (char c; i >= 0; i--) { |
| c = in.charAt(i); |
| if (c < 0x80 && pos >= bufferOffset) { |
| UnsafeUtil.putByte(pos--, (byte) c); |
| } else if (c < 0x800 && pos > bufferOffset) { // 11 bits, two UTF-8 bytes |
| UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & c))); |
| UnsafeUtil.putByte(pos--, (byte) ((0xF << 6) | (c >>> 6))); |
| } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) |
| && pos > bufferOffset + 1) { |
| // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes |
| UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & c))); |
| UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & (c >>> 6)))); |
| UnsafeUtil.putByte(pos--, (byte) ((0xF << 5) | (c >>> 12))); |
| } else if (pos > bufferOffset + 2) { |
| // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, |
| // four UTF-8 bytes |
| final char high; |
| if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) { |
| throw new Utf8.UnpairedSurrogateException(i - 1, i); |
| } |
| i--; |
| int codePoint = Character.toCodePoint(high, c); |
| UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & codePoint))); |
| UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); |
| UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); |
| UnsafeUtil.putByte(pos--, (byte) ((0xF << 4) | (codePoint >>> 18))); |
| } else { |
| // Buffer is full - allocate a new one and revisit the current character. |
| requireSpace(i); |
| i++; |
| } |
| } |
| } |
| |
| @Override |
| public void write(byte value) { |
| UnsafeUtil.putByte(pos--, value); |
| } |
| |
| @Override |
| public void write(byte[] value, int offset, int length) { |
| if (spaceLeft() < length) { |
| nextBuffer(length); |
| } |
| |
| pos -= length; |
| buffer.position(bufferPos() + 1); |
| buffer.put(value, offset, length); |
| } |
| |
| @Override |
| public void writeLazy(byte[] value, int offset, int length) { |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value, offset, length)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| return; |
| } |
| |
| pos -= length; |
| buffer.position(bufferPos() + 1); |
| buffer.put(value, offset, length); |
| } |
| |
| @Override |
| public void write(ByteBuffer value) { |
| int length = value.remaining(); |
| if (spaceLeft() < length) { |
| nextBuffer(length); |
| } |
| |
| pos -= length; |
| buffer.position(bufferPos() + 1); |
| buffer.put(value); |
| } |
| |
| @Override |
| public void writeLazy(ByteBuffer value) { |
| int length = value.remaining(); |
| if (spaceLeft() < length) { |
| // We consider the value to be immutable (likely the internals of a ByteString). Just |
| // wrap it in a Netty buffer and add it to the output buffer. |
| totalDoneBytes += length; |
| buffers.addFirst(AllocatedBuffer.wrap(value)); |
| |
| // Advance the writer to the next buffer. |
| // TODO(nathanmittler): Consider slicing if space available above some threshold. |
| nextBuffer(); |
| return; |
| } |
| |
| pos -= length; |
| buffer.position(bufferPos() + 1); |
| buffer.put(value); |
| } |
| |
| @Override |
| void requireSpace(int size) { |
| if (spaceLeft() < size) { |
| nextBuffer(size); |
| } |
| } |
| } |
| } |