• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 import com.google.protobuf.ByteString;
32 import com.google.protobuf.CodedInputStream;
33 import com.google.protobuf.ExtensionRegistryLite;
34 import com.google.protobuf.InvalidProtocolBufferException;
35 import com.google.protobuf.MessageLite;
36 import com.google.protobuf.Parser;
37 import com.google.protobuf.conformance.Conformance;
38 import com.google.protobuf_test_messages.proto2.TestMessagesProto2;
39 import com.google.protobuf_test_messages.proto2.TestMessagesProto2.TestAllTypesProto2;
40 import com.google.protobuf_test_messages.proto3.TestMessagesProto3;
41 import com.google.protobuf_test_messages.proto3.TestMessagesProto3.TestAllTypesProto3;
42 import java.nio.ByteBuffer;
43 import java.util.ArrayList;
44 
45 class ConformanceJavaLite {
46   private int testCount = 0;
47 
readFromStdin(byte[] buf, int len)48   private boolean readFromStdin(byte[] buf, int len) throws Exception {
49     int ofs = 0;
50     while (len > 0) {
51       int read = System.in.read(buf, ofs, len);
52       if (read == -1) {
53         return false; // EOF
54       }
55       ofs += read;
56       len -= read;
57     }
58 
59     return true;
60   }
61 
writeToStdout(byte[] buf)62   private void writeToStdout(byte[] buf) throws Exception {
63     System.out.write(buf);
64   }
65 
66   // Returns -1 on EOF (the actual values will always be positive).
readLittleEndianIntFromStdin()67   private int readLittleEndianIntFromStdin() throws Exception {
68     byte[] buf = new byte[4];
69     if (!readFromStdin(buf, 4)) {
70       return -1;
71     }
72     return (buf[0] & 0xff)
73         | ((buf[1] & 0xff) << 8)
74         | ((buf[2] & 0xff) << 16)
75         | ((buf[3] & 0xff) << 24);
76   }
77 
writeLittleEndianIntToStdout(int val)78   private void writeLittleEndianIntToStdout(int val) throws Exception {
79     byte[] buf = new byte[4];
80     buf[0] = (byte) val;
81     buf[1] = (byte) (val >> 8);
82     buf[2] = (byte) (val >> 16);
83     buf[3] = (byte) (val >> 24);
84     writeToStdout(buf);
85   }
86 
87   private enum BinaryDecoderType {
88     BTYE_STRING_DECODER,
89     BYTE_ARRAY_DECODER,
90     ARRAY_BYTE_BUFFER_DECODER,
91     READONLY_ARRAY_BYTE_BUFFER_DECODER,
92     DIRECT_BYTE_BUFFER_DECODER,
93     READONLY_DIRECT_BYTE_BUFFER_DECODER,
94     INPUT_STREAM_DECODER;
95   }
96 
97   private static class BinaryDecoder<T extends MessageLite> {
decode( ByteString bytes, BinaryDecoderType type, Parser<T> parser, ExtensionRegistryLite extensions)98     public T decode(
99         ByteString bytes,
100         BinaryDecoderType type,
101         Parser<T> parser,
102         ExtensionRegistryLite extensions)
103         throws InvalidProtocolBufferException {
104       switch (type) {
105         case BTYE_STRING_DECODER:
106         case BYTE_ARRAY_DECODER:
107           return parser.parseFrom(bytes, extensions);
108         case ARRAY_BYTE_BUFFER_DECODER:
109           {
110             ByteBuffer buffer = ByteBuffer.allocate(bytes.size());
111             bytes.copyTo(buffer);
112             buffer.flip();
113             return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
114           }
115         case READONLY_ARRAY_BYTE_BUFFER_DECODER:
116           {
117             return parser.parseFrom(
118                 CodedInputStream.newInstance(bytes.asReadOnlyByteBuffer()), extensions);
119           }
120         case DIRECT_BYTE_BUFFER_DECODER:
121           {
122             ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
123             bytes.copyTo(buffer);
124             buffer.flip();
125             return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
126           }
127         case READONLY_DIRECT_BYTE_BUFFER_DECODER:
128           {
129             ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
130             bytes.copyTo(buffer);
131             buffer.flip();
132             return parser.parseFrom(
133                 CodedInputStream.newInstance(buffer.asReadOnlyBuffer()), extensions);
134           }
135         case INPUT_STREAM_DECODER:
136           {
137             return parser.parseFrom(bytes.newInput(), extensions);
138           }
139       }
140       return null;
141     }
142   }
143 
parseBinary( ByteString bytes, Parser<T> parser, ExtensionRegistryLite extensions)144   private <T extends MessageLite> T parseBinary(
145       ByteString bytes, Parser<T> parser, ExtensionRegistryLite extensions)
146       throws InvalidProtocolBufferException {
147     ArrayList<T> messages = new ArrayList<>();
148     ArrayList<InvalidProtocolBufferException> exceptions = new ArrayList<>();
149 
150     for (int i = 0; i < BinaryDecoderType.values().length; i++) {
151       messages.add(null);
152       exceptions.add(null);
153     }
154     if (messages.isEmpty()) {
155       throw new RuntimeException("binary decoder types missing");
156     }
157 
158     BinaryDecoder<T> decoder = new BinaryDecoder<>();
159 
160     boolean hasMessage = false;
161     boolean hasException = false;
162     for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
163       try {
164         messages.set(i, decoder.decode(bytes, BinaryDecoderType.values()[i], parser, extensions));
165         hasMessage = true;
166       } catch (InvalidProtocolBufferException e) {
167         exceptions.set(i, e);
168         hasException = true;
169       }
170     }
171 
172     if (hasMessage && hasException) {
173       StringBuilder sb =
174           new StringBuilder("Binary decoders disagreed on whether the payload was valid.\n");
175       for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
176         sb.append(BinaryDecoderType.values()[i].name());
177         if (messages.get(i) != null) {
178           sb.append(" accepted the payload.\n");
179         } else {
180           sb.append(" rejected the payload.\n");
181         }
182       }
183       throw new RuntimeException(sb.toString());
184     }
185 
186     if (hasException) {
187       // We do not check if exceptions are equal. Different implementations may return different
188       // exception messages. Throw an arbitrary one out instead.
189       InvalidProtocolBufferException exception = null;
190       for (InvalidProtocolBufferException e : exceptions) {
191         if (exception != null) {
192           exception.addSuppressed(e);
193         } else {
194           exception = e;
195         }
196       }
197       throw exception;
198     }
199 
200     // Fast path comparing all the messages with the first message, assuming equality being
201     // symmetric and transitive.
202     boolean allEqual = true;
203     for (int i = 1; i < messages.size(); ++i) {
204       if (!messages.get(0).equals(messages.get(i))) {
205         allEqual = false;
206         break;
207       }
208     }
209 
210     // Slow path: compare and find out all unequal pairs.
211     if (!allEqual) {
212       StringBuilder sb = new StringBuilder();
213       for (int i = 0; i < messages.size() - 1; ++i) {
214         for (int j = i + 1; j < messages.size(); ++j) {
215           if (!messages.get(i).equals(messages.get(j))) {
216             sb.append(BinaryDecoderType.values()[i].name())
217                 .append(" and ")
218                 .append(BinaryDecoderType.values()[j].name())
219                 .append(" parsed the payload differently.\n");
220           }
221         }
222       }
223       throw new RuntimeException(sb.toString());
224     }
225 
226     return messages.get(0);
227   }
228 
doTest(Conformance.ConformanceRequest request)229   private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
230     com.google.protobuf.MessageLite testMessage;
231     boolean isProto3 =
232         request.getMessageType().equals("protobuf_test_messages.proto3.TestAllTypesProto3");
233     boolean isProto2 =
234         request.getMessageType().equals("protobuf_test_messages.proto2.TestAllTypesProto2");
235 
236     switch (request.getPayloadCase()) {
237       case PROTOBUF_PAYLOAD:
238         {
239           if (isProto3) {
240             try {
241               ExtensionRegistryLite extensions = ExtensionRegistryLite.newInstance();
242               TestMessagesProto3.registerAllExtensions(extensions);
243               testMessage =
244                   parseBinary(
245                       request.getProtobufPayload(), TestAllTypesProto3.parser(), extensions);
246             } catch (InvalidProtocolBufferException e) {
247               return Conformance.ConformanceResponse.newBuilder()
248                   .setParseError(e.getMessage())
249                   .build();
250             }
251           } else if (isProto2) {
252             try {
253               ExtensionRegistryLite extensions = ExtensionRegistryLite.newInstance();
254               TestMessagesProto2.registerAllExtensions(extensions);
255               testMessage =
256                   parseBinary(
257                       request.getProtobufPayload(), TestAllTypesProto2.parser(), extensions);
258             } catch (InvalidProtocolBufferException e) {
259               return Conformance.ConformanceResponse.newBuilder()
260                   .setParseError(e.getMessage())
261                   .build();
262             }
263           } else {
264             throw new RuntimeException("Protobuf request doesn't have specific payload type.");
265           }
266           break;
267         }
268       case JSON_PAYLOAD:
269         {
270           return Conformance.ConformanceResponse.newBuilder()
271               .setSkipped("Lite runtime does not support JSON format.")
272               .build();
273         }
274       case TEXT_PAYLOAD:
275         {
276           return Conformance.ConformanceResponse.newBuilder()
277               .setSkipped("Lite runtime does not support Text format.")
278               .build();
279         }
280       case PAYLOAD_NOT_SET:
281         {
282           throw new RuntimeException("Request didn't have payload.");
283         }
284       default:
285         {
286           throw new RuntimeException("Unexpected payload case.");
287         }
288     }
289 
290     switch (request.getRequestedOutputFormat()) {
291       case UNSPECIFIED:
292         throw new RuntimeException("Unspecified output format.");
293 
294       case PROTOBUF:
295         return Conformance.ConformanceResponse.newBuilder()
296             .setProtobufPayload(testMessage.toByteString())
297             .build();
298 
299       case JSON:
300         return Conformance.ConformanceResponse.newBuilder()
301             .setSkipped("Lite runtime does not support JSON format.")
302             .build();
303 
304       case TEXT_FORMAT:
305         return Conformance.ConformanceResponse.newBuilder()
306             .setSkipped("Lite runtime does not support Text format.")
307             .build();
308       default:
309         {
310           throw new RuntimeException("Unexpected request output.");
311         }
312     }
313   }
314 
doTestIo()315   private boolean doTestIo() throws Exception {
316     int bytes = readLittleEndianIntFromStdin();
317 
318     if (bytes == -1) {
319       return false; // EOF
320     }
321 
322     byte[] serializedInput = new byte[bytes];
323 
324     if (!readFromStdin(serializedInput, bytes)) {
325       throw new RuntimeException("Unexpected EOF from test program.");
326     }
327 
328     Conformance.ConformanceRequest request =
329         Conformance.ConformanceRequest.parseFrom(serializedInput);
330     Conformance.ConformanceResponse response = doTest(request);
331     byte[] serializedOutput = response.toByteArray();
332 
333     writeLittleEndianIntToStdout(serializedOutput.length);
334     writeToStdout(serializedOutput);
335 
336     return true;
337   }
338 
run()339   public void run() throws Exception {
340     while (doTestIo()) {
341       this.testCount++;
342     }
343 
344     System.err.println(
345         "ConformanceJavaLite: received EOF from test runner after " + this.testCount + " tests");
346   }
347 
main(String[] args)348   public static void main(String[] args) throws Exception {
349     new ConformanceJavaLite().run();
350   }
351 }
352