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