1 /*
2  * Copyright 2014 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.protobuf.nano;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.protobuf.nano.CodedInputByteBufferNano;
22 import com.google.protobuf.nano.MessageNano;
23 import io.grpc.MethodDescriptor.Marshaller;
24 import io.grpc.Status;
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 
30 /**
31  * Utility methods for using nano proto with grpc.
32  */
33 public final class NanoUtils {
34 
NanoUtils()35   private NanoUtils() {}
36 
37   /**
38    * Adapt {@code parser} to a {@link Marshaller}.
39    *
40    * @since 1.0.0
41    */
marshaller(MessageNanoFactory<T> factory)42   public static <T extends MessageNano> Marshaller<T> marshaller(MessageNanoFactory<T> factory) {
43     return new MessageMarshaller<T>(factory);
44   }
45 
46   private static final class MessageMarshaller<T extends MessageNano> implements Marshaller<T> {
47     private static final int BUF_SIZE = 8192;
48 
49     private final MessageNanoFactory<T> factory;
50 
MessageMarshaller(MessageNanoFactory<T> factory)51     MessageMarshaller(MessageNanoFactory<T> factory) {
52       this.factory = factory;
53     }
54 
55     @Override
stream(T value)56     public InputStream stream(T value) {
57       return new NanoProtoInputStream(value);
58     }
59 
60     @Override
parse(InputStream stream)61     public T parse(InputStream stream) {
62       try {
63         // TODO(simonma): Investigate whether we can do 0-copy here.
64         CodedInputByteBufferNano input =
65             CodedInputByteBufferNano.newInstance(toByteArray(stream));
66         input.setSizeLimit(Integer.MAX_VALUE);
67         T message = factory.newInstance();
68         message.mergeFrom(input);
69         return message;
70       } catch (IOException ipbe) {
71         throw Status.INTERNAL.withDescription("Failed parsing nano proto message").withCause(ipbe)
72             .asRuntimeException();
73       }
74     }
75 
76     // Copied from guava com.google.common.io.ByteStreams because its API is unstable (beta)
toByteArray(InputStream in)77     private static byte[] toByteArray(InputStream in) throws IOException {
78       ByteArrayOutputStream out = new ByteArrayOutputStream();
79       copy(in, out);
80       return out.toByteArray();
81     }
82 
83     // Copied from guava com.google.common.io.ByteStreams because its API is unstable (beta)
copy(InputStream from, OutputStream to)84     private static long copy(InputStream from, OutputStream to) throws IOException {
85       checkNotNull(from);
86       checkNotNull(to);
87       byte[] buf = new byte[BUF_SIZE];
88       long total = 0;
89       while (true) {
90         int r = from.read(buf);
91         if (r == -1) {
92           break;
93         }
94         to.write(buf, 0, r);
95         total += r;
96       }
97       return total;
98     }
99   }
100 }
101