| Index: third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
|
| diff --git a/third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0cc38175f908cd41bb6b2c4e46723d676b373fcb
|
| --- /dev/null
|
| +++ b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
|
| @@ -0,0 +1,145 @@
|
| +// 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 java.lang.Math.max;
|
| +import static java.lang.Math.min;
|
| +
|
| +import java.io.FileOutputStream;
|
| +import java.io.IOException;
|
| +import java.io.OutputStream;
|
| +import java.lang.ref.SoftReference;
|
| +import java.nio.ByteBuffer;
|
| +
|
| +/**
|
| + * Utility class to provide efficient writing of {@link ByteBuffer}s to {@link OutputStream}s.
|
| + */
|
| +final class ByteBufferWriter {
|
| + private ByteBufferWriter() {}
|
| +
|
| + /**
|
| + * Minimum size for a cached buffer. This prevents us from allocating buffers that are too
|
| + * small to be easily reused.
|
| + */
|
| + // TODO(nathanmittler): tune this property or allow configuration?
|
| + private static final int MIN_CACHED_BUFFER_SIZE = 1024;
|
| +
|
| + /**
|
| + * Maximum size for a cached buffer. If a larger buffer is required, it will be allocated
|
| + * but not cached.
|
| + */
|
| + // TODO(nathanmittler): tune this property or allow configuration?
|
| + private static final int MAX_CACHED_BUFFER_SIZE = 16 * 1024;
|
| +
|
| + /**
|
| + * The fraction of the requested buffer size under which the buffer will be reallocated.
|
| + */
|
| + // TODO(nathanmittler): tune this property or allow configuration?
|
| + private static final float BUFFER_REALLOCATION_THRESHOLD = 0.5f;
|
| +
|
| + /**
|
| + * Keeping a soft reference to a thread-local buffer. This buffer is used for writing a
|
| + * {@link ByteBuffer} to an {@link OutputStream} when no zero-copy alternative was available.
|
| + * Using a "soft" reference since VMs may keep this reference around longer than "weak"
|
| + * (e.g. HotSpot will maintain soft references until memory pressure warrants collection).
|
| + */
|
| + private static final ThreadLocal<SoftReference<byte[]>> BUFFER =
|
| + new ThreadLocal<SoftReference<byte[]>>();
|
| +
|
| + /**
|
| + * For testing purposes only. Clears the cached buffer to force a new allocation on the next
|
| + * invocation.
|
| + */
|
| + static void clearCachedBuffer() {
|
| + BUFFER.set(null);
|
| + }
|
| +
|
| + /**
|
| + * Writes the remaining content of the buffer to the given stream. The buffer {@code position}
|
| + * will remain unchanged by this method.
|
| + */
|
| + static void write(ByteBuffer buffer, OutputStream output) throws IOException {
|
| + final int initialPos = buffer.position();
|
| + try {
|
| + if (buffer.hasArray()) {
|
| + // Optimized write for array-backed buffers.
|
| + // Note that we're taking the risk that a malicious OutputStream could modify the array.
|
| + output.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
| + } else if (output instanceof FileOutputStream) {
|
| + // Use a channel to write out the ByteBuffer. This will automatically empty the buffer.
|
| + ((FileOutputStream) output).getChannel().write(buffer);
|
| + } else {
|
| + // Read all of the data from the buffer to an array.
|
| + // TODO(nathanmittler): Consider performance improvements for other "known" stream types.
|
| + final byte[] array = getOrCreateBuffer(buffer.remaining());
|
| + while (buffer.hasRemaining()) {
|
| + int length = min(buffer.remaining(), array.length);
|
| + buffer.get(array, 0, length);
|
| + output.write(array, 0, length);
|
| + }
|
| + }
|
| + } finally {
|
| + // Restore the initial position.
|
| + buffer.position(initialPos);
|
| + }
|
| + }
|
| +
|
| + private static byte[] getOrCreateBuffer(int requestedSize) {
|
| + requestedSize = max(requestedSize, MIN_CACHED_BUFFER_SIZE);
|
| +
|
| + byte[] buffer = getBuffer();
|
| + // Only allocate if we need to.
|
| + if (buffer == null || needToReallocate(requestedSize, buffer.length)) {
|
| + buffer = new byte[requestedSize];
|
| +
|
| + // Only cache the buffer if it's not too big.
|
| + if (requestedSize <= MAX_CACHED_BUFFER_SIZE) {
|
| + setBuffer(buffer);
|
| + }
|
| + }
|
| + return buffer;
|
| + }
|
| +
|
| + private static boolean needToReallocate(int requestedSize, int bufferLength) {
|
| + // First check against just the requested length to avoid the multiply.
|
| + return bufferLength < requestedSize
|
| + && bufferLength < requestedSize * BUFFER_REALLOCATION_THRESHOLD;
|
| + }
|
| +
|
| + private static byte[] getBuffer() {
|
| + SoftReference<byte[]> sr = BUFFER.get();
|
| + return sr == null ? null : sr.get();
|
| + }
|
| +
|
| + private static void setBuffer(byte[] value) {
|
| + BUFFER.set(new SoftReference<byte[]>(value));
|
| + }
|
| +}
|
|
|