-
Notifications
You must be signed in to change notification settings - Fork 4k
protobuf: zero copy into protobuf #7250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
c8810ad
17d6dbb
83f0448
69a1ddf
88d2b6f
94b5124
ce8969e
a16b6ae
0cb6ea8
f9aaffc
8a0c7a2
8fb4534
58165d6
e2b3991
8959de5
86414ad
093e435
c9c123c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Copyright 2020 The gRPC Authors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package io.grpc; | ||
|
|
||
| import java.nio.ByteBuffer; | ||
| import javax.annotation.Nullable; | ||
|
|
||
| /** | ||
| * An {@link java.io.InputStream} or alike that supports the operation of reading its content into | ||
| * {@link ByteBuffer}s. | ||
| * | ||
| * <p>Usually used for implementations (directly or indirectly) backed by {@link ByteBuffer}s so | ||
| * that read operations avoid making an extra copy by returning {@link ByteBuffer}s sharing content | ||
| * with the backing {@link ByteBuffer}s. | ||
| */ | ||
| public interface ByteBufferReadable { | ||
|
|
||
|
|
||
| /** | ||
| * Reads up to a total of {@code length} bytes as {@link ByteBuffer}s. | ||
| * | ||
| * @param length the maximum number of bytes to be read. | ||
| * @return {@link ByteBuffer}s that contains the bytes being read or {@code null} if no more | ||
| * bytes can be read. | ||
| */ | ||
| @Nullable | ||
| Iterable<ByteBuffer> readByteBuffers(int length); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,8 @@ | |
| import java.nio.Buffer; | ||
| import java.nio.ByteBuffer; | ||
| import java.util.ArrayDeque; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Queue; | ||
|
|
||
| /** | ||
|
|
@@ -59,6 +61,35 @@ public void addBuffer(ReadableBuffer buffer) { | |
| compositeBuffer.close(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean shouldUseByteBuffer() { | ||
| for (ReadableBuffer buf : buffers) { | ||
| if (!buf.shouldUseByteBuffer()) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public List<ByteBuffer> readByteBuffers(int length) { | ||
| checkReadable(length); | ||
| readableBytes -= length; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe better to decrement this within the loop so that the buffer state remains consistent in case of runtime exception? Also how about a special case (should be common): if (buffers.size() == 1) {
return (readableBytes == 0 ? buffers.poll() : buffers.peek()).readByteBuffers(length);
} |
||
|
|
||
| List<ByteBuffer> res = new ArrayList<>(); | ||
| while (length > 0) { | ||
| ReadableBuffer buffer = buffers.peek(); | ||
| int readLength = length; | ||
| if (buffer.readableBytes() <= length) { | ||
| readLength = buffer.readableBytes(); | ||
| buffers.poll(); | ||
|
||
| } | ||
| res.addAll(buffer.readByteBuffers(readLength)); | ||
| length -= readLength; | ||
| } | ||
| return res; | ||
| } | ||
|
|
||
| @Override | ||
| public int readableBytes() { | ||
| return readableBytes; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| import java.io.IOException; | ||
| import java.io.OutputStream; | ||
| import java.nio.ByteBuffer; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Interface for an abstract byte buffer. Buffers are intended to be a read-only, except for the | ||
|
|
@@ -102,6 +103,22 @@ public interface ReadableBuffer extends Closeable { | |
| */ | ||
| ReadableBuffer readBytes(int length); | ||
|
|
||
| /** | ||
| * Indicates whether or not this buffer supports {@link #readByteBuffers} operation that returns | ||
| * buffer's content as {@link ByteBuffer}s without making an extra copy. | ||
| */ | ||
| boolean shouldUseByteBuffer(); | ||
|
|
||
| /** | ||
| * Reads {@code length} bytes as {@link ByteBuffer}s. This is an optional method, so callers | ||
| * should first check {@link #shouldUseByteBuffer}. | ||
| * | ||
| * @param length the total number of bytes to contain in the returned {@link ByteBuffer}s. | ||
| * @throws UnsupportedOperationException the buffer does not support this method | ||
| * @throws IndexOutOfBoundsException if required bytes are not readable | ||
| */ | ||
| List<ByteBuffer> readByteBuffers(int length); | ||
|
||
|
|
||
| /** | ||
| * Indicates whether or not this buffer exposes a backing array. | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,8 @@ | |
| import java.io.IOException; | ||
| import java.io.OutputStream; | ||
| import java.nio.ByteBuffer; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * A {@link java.nio.Buffer} implementation that is backed by a Netty {@link ByteBuf}. This class | ||
|
|
@@ -79,6 +81,21 @@ public NettyReadableBuffer readBytes(int length) { | |
| return new NettyReadableBuffer(buffer.readRetainedSlice(length)); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean shouldUseByteBuffer() { | ||
| return buffer.nioBufferCount() > 0; | ||
| } | ||
|
|
||
| @Override | ||
| public List<ByteBuffer> readByteBuffers(int length) { | ||
| if (buffer.readableBytes() < length) { | ||
| throw new IndexOutOfBoundsException(); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return empty list here if |
||
| List<ByteBuffer> res = Arrays.asList(buffer.nioBuffers(buffer.readerIndex(), length)); | ||
|
||
| buffer.skipBytes(length); | ||
| return res; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean hasArray() { | ||
| return buffer.hasArray(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ | |
| import com.google.protobuf.InvalidProtocolBufferException; | ||
| import com.google.protobuf.MessageLite; | ||
| import com.google.protobuf.Parser; | ||
| import io.grpc.ByteBufferReadable; | ||
| import io.grpc.ExperimentalApi; | ||
| import io.grpc.KnownLength; | ||
| import io.grpc.Metadata; | ||
|
|
@@ -173,7 +174,15 @@ public T parse(InputStream stream) { | |
| try { | ||
| if (stream instanceof KnownLength) { | ||
| int size = stream.available(); | ||
| if (size > 0 && size <= DEFAULT_MAX_MESSAGE_SIZE) { | ||
| if (size <= 0) { | ||
| return defaultInstance; | ||
| } | ||
| // TODO(chengyuanzhang): we may still want to go with the byte array approach for small | ||
| // messages. | ||
| if (stream instanceof ByteBufferReadable) { | ||
| cis = CodedInputStream.newInstance( | ||
| ((ByteBufferReadable) stream).readByteBuffers(size)); | ||
|
||
| } else if (size < DEFAULT_MAX_MESSAGE_SIZE) { | ||
| Reference<byte[]> ref; | ||
| // buf should not be used after this method has returned. | ||
| byte[] buf; | ||
|
|
@@ -197,8 +206,6 @@ public T parse(InputStream stream) { | |
| throw new RuntimeException("size inaccurate: " + size + " != " + position); | ||
| } | ||
| cis = CodedInputStream.newInstance(buf, 0, size); | ||
| } else if (size == 0) { | ||
| return defaultInstance; | ||
| } | ||
| } | ||
| } catch (IOException e) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe return
Collections.emptyList()instead ofnull?