| OLD | NEW |
| 1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
| 2 // Copyright 2008 Google Inc. All rights reserved. | 2 // Copyright 2008 Google Inc. All rights reserved. |
| 3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
| 4 // | 4 // |
| 5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
| 6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
| 7 // met: | 7 // met: |
| 8 // | 8 // |
| 9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
| 10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | 30 |
| 31 #include <google/protobuf/util/json_util.h> | 31 #include <google/protobuf/util/json_util.h> |
| 32 | 32 |
| 33 #include <list> | 33 #include <list> |
| 34 #include <string> | 34 #include <string> |
| 35 | 35 |
| 36 #include <google/protobuf/io/zero_copy_stream.h> | 36 #include <google/protobuf/io/zero_copy_stream.h> |
| 37 #include <google/protobuf/descriptor_database.h> | |
| 38 #include <google/protobuf/dynamic_message.h> | |
| 39 #include <google/protobuf/util/json_format_proto3.pb.h> | 37 #include <google/protobuf/util/json_format_proto3.pb.h> |
| 40 #include <google/protobuf/util/type_resolver.h> | 38 #include <google/protobuf/util/type_resolver.h> |
| 41 #include <google/protobuf/util/type_resolver_util.h> | 39 #include <google/protobuf/util/type_resolver_util.h> |
| 42 #include <gtest/gtest.h> | 40 #include <gtest/gtest.h> |
| 43 | 41 |
| 44 namespace google { | 42 namespace google { |
| 45 namespace protobuf { | 43 namespace protobuf { |
| 46 namespace util { | 44 namespace util { |
| 47 namespace { | 45 namespace { |
| 48 | 46 |
| 49 using proto3::FOO; | 47 using proto3::FOO; |
| 50 using proto3::BAR; | 48 using proto3::BAR; |
| 51 using proto3::TestMessage; | 49 using proto3::TestMessage; |
| 52 using proto3::TestMap; | 50 using proto3::TestMap; |
| 53 | 51 |
| 54 static const char kTypeUrlPrefix[] = "type.googleapis.com"; | 52 static const char kTypeUrlPrefix[] = "type.googleapis.com"; |
| 55 | 53 |
| 56 static string GetTypeUrl(const Descriptor* message) { | 54 static string GetTypeUrl(const Descriptor* message) { |
| 57 return string(kTypeUrlPrefix) + "/" + message->full_name(); | 55 return string(kTypeUrlPrefix) + "/" + message->full_name(); |
| 58 } | 56 } |
| 59 | 57 |
| 60 // As functions defined in json_util.h are just thin wrappers around the | 58 // As functions defined in json_util.h are just thin wrappers around the |
| 61 // JSON conversion code in //net/proto2/util/converter, in this test we | 59 // JSON conversion code in //net/proto2/util/converter, in this test we |
| 62 // only cover some very basic cases to make sure the wrappers have forwarded | 60 // only cover some very basic cases to make sure the wrappers have forwarded |
| 63 // parameters to the underlying implementation correctly. More detailed | 61 // parameters to the underlying implementation correctly. More detailed |
| 64 // tests are contained in the //net/proto2/util/converter directory. | 62 // tests are contained in the //net/proto2/util/converter directory. |
| 65 class JsonUtilTest : public testing::Test { | 63 class JsonUtilTest : public testing::Test { |
| 66 protected: | 64 protected: |
| 67 JsonUtilTest() { | 65 JsonUtilTest() { |
| 66 resolver_.reset(NewTypeResolverForDescriptorPool( |
| 67 kTypeUrlPrefix, DescriptorPool::generated_pool())); |
| 68 } | 68 } |
| 69 | 69 |
| 70 string ToJson(const Message& message, const JsonPrintOptions& options) { | 70 string ToJson(const Message& message, const JsonOptions& options) { |
| 71 string result; | 71 string result; |
| 72 GOOGLE_CHECK_OK(MessageToJsonString(message, &result, options)); | 72 GOOGLE_CHECK_OK(BinaryToJsonString(resolver_.get(), |
| 73 GetTypeUrl(message.GetDescriptor()), |
| 74 message.SerializeAsString(), &result, options)); |
| 73 return result; | 75 return result; |
| 74 } | 76 } |
| 75 | 77 |
| 76 bool FromJson(const string& json, Message* message, | |
| 77 const JsonParseOptions& options) { | |
| 78 return JsonStringToMessage(json, message, options).ok(); | |
| 79 } | |
| 80 | |
| 81 bool FromJson(const string& json, Message* message) { | 78 bool FromJson(const string& json, Message* message) { |
| 82 return FromJson(json, message, JsonParseOptions()); | 79 string binary; |
| 80 if (!JsonToBinaryString(resolver_.get(), |
| 81 GetTypeUrl(message->GetDescriptor()), json, &binary) |
| 82 .ok()) { |
| 83 return false; |
| 84 } |
| 85 return message->ParseFromString(binary); |
| 83 } | 86 } |
| 84 | 87 |
| 85 google::protobuf::scoped_ptr<TypeResolver> resolver_; | 88 google::protobuf::scoped_ptr<TypeResolver> resolver_; |
| 86 }; | 89 }; |
| 87 | 90 |
| 88 TEST_F(JsonUtilTest, TestWhitespaces) { | 91 TEST_F(JsonUtilTest, TestWhitespaces) { |
| 89 TestMessage m; | 92 TestMessage m; |
| 90 m.mutable_message_value(); | 93 m.mutable_message_value(); |
| 91 | 94 |
| 92 JsonPrintOptions options; | 95 JsonOptions options; |
| 93 EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options)); | 96 EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options)); |
| 94 options.add_whitespace = true; | 97 options.add_whitespace = true; |
| 95 EXPECT_EQ( | 98 EXPECT_EQ( |
| 96 "{\n" | 99 "{\n" |
| 97 " \"messageValue\": {}\n" | 100 " \"messageValue\": {}\n" |
| 98 "}\n", | 101 "}\n", |
| 99 ToJson(m, options)); | 102 ToJson(m, options)); |
| 100 } | 103 } |
| 101 | 104 |
| 102 TEST_F(JsonUtilTest, TestDefaultValues) { | 105 TEST_F(JsonUtilTest, TestDefaultValues) { |
| 103 TestMessage m; | 106 TestMessage m; |
| 104 JsonPrintOptions options; | 107 JsonOptions options; |
| 105 EXPECT_EQ("{}", ToJson(m, options)); | 108 EXPECT_EQ("{}", ToJson(m, options)); |
| 106 options.always_print_primitive_fields = true; | 109 options.always_print_primitive_fields = true; |
| 107 EXPECT_EQ( | 110 EXPECT_EQ( |
| 108 "{\"boolValue\":false," | 111 "{\"boolValue\":false," |
| 109 "\"int32Value\":0," | 112 "\"int32Value\":0," |
| 110 "\"int64Value\":\"0\"," | 113 "\"int64Value\":\"0\"," |
| 111 "\"uint32Value\":0," | 114 "\"uint32Value\":0," |
| 112 "\"uint64Value\":\"0\"," | 115 "\"uint64Value\":\"0\"," |
| 113 "\"floatValue\":0," | 116 "\"floatValue\":0," |
| 114 "\"doubleValue\":0," | 117 "\"doubleValue\":0," |
| 115 "\"stringValue\":\"\"," | 118 "\"stringValue\":\"\"," |
| 116 "\"bytesValue\":\"\"," | 119 "\"bytesValue\":\"\"," |
| 117 "\"enumValue\":\"FOO\"," | 120 "\"enumValue\":\"FOO\"," |
| 118 "\"repeatedBoolValue\":[]," | 121 "\"repeatedBoolValue\":[]," |
| 119 "\"repeatedInt32Value\":[]," | 122 "\"repeatedInt32Value\":[]," |
| 120 "\"repeatedInt64Value\":[]," | 123 "\"repeatedInt64Value\":[]," |
| 121 "\"repeatedUint32Value\":[]," | 124 "\"repeatedUint32Value\":[]," |
| 122 "\"repeatedUint64Value\":[]," | 125 "\"repeatedUint64Value\":[]," |
| 123 "\"repeatedFloatValue\":[]," | 126 "\"repeatedFloatValue\":[]," |
| 124 "\"repeatedDoubleValue\":[]," | 127 "\"repeatedDoubleValue\":[]," |
| 125 "\"repeatedStringValue\":[]," | 128 "\"repeatedStringValue\":[]," |
| 126 "\"repeatedBytesValue\":[]," | 129 "\"repeatedBytesValue\":[]," |
| 127 "\"repeatedEnumValue\":[]," | 130 "\"repeatedEnumValue\":[]," |
| 128 "\"repeatedMessageValue\":[]" | 131 "\"repeatedMessageValue\":[]" |
| 129 "}", | 132 "}", |
| 130 ToJson(m, options)); | 133 ToJson(m, options)); |
| 131 | |
| 132 options.always_print_primitive_fields = true; | |
| 133 m.set_string_value("i am a test string value"); | |
| 134 m.set_bytes_value("i am a test bytes value"); | |
| 135 EXPECT_EQ( | |
| 136 "{\"boolValue\":false," | |
| 137 "\"int32Value\":0," | |
| 138 "\"int64Value\":\"0\"," | |
| 139 "\"uint32Value\":0," | |
| 140 "\"uint64Value\":\"0\"," | |
| 141 "\"floatValue\":0," | |
| 142 "\"doubleValue\":0," | |
| 143 "\"stringValue\":\"i am a test string value\"," | |
| 144 "\"bytesValue\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\"," | |
| 145 "\"enumValue\":\"FOO\"," | |
| 146 "\"repeatedBoolValue\":[]," | |
| 147 "\"repeatedInt32Value\":[]," | |
| 148 "\"repeatedInt64Value\":[]," | |
| 149 "\"repeatedUint32Value\":[]," | |
| 150 "\"repeatedUint64Value\":[]," | |
| 151 "\"repeatedFloatValue\":[]," | |
| 152 "\"repeatedDoubleValue\":[]," | |
| 153 "\"repeatedStringValue\":[]," | |
| 154 "\"repeatedBytesValue\":[]," | |
| 155 "\"repeatedEnumValue\":[]," | |
| 156 "\"repeatedMessageValue\":[]" | |
| 157 "}", | |
| 158 ToJson(m, options)); | |
| 159 } | 134 } |
| 160 | 135 |
| 161 TEST_F(JsonUtilTest, ParseMessage) { | 136 TEST_F(JsonUtilTest, ParseMessage) { |
| 162 // Some random message but good enough to verify that the parsing warpper | 137 // Some random message but good enough to verify that the parsing warpper |
| 163 // functions are working properly. | 138 // functions are working properly. |
| 164 string input = | 139 string input = |
| 165 "{\n" | 140 "{\n" |
| 166 " \"int32Value\": 1024,\n" | 141 " \"int32Value\": 1024,\n" |
| 167 " \"repeatedInt32Value\": [1, 2],\n" | 142 " \"repeatedInt32Value\": [1, 2],\n" |
| 168 " \"messageValue\": {\n" | 143 " \"messageValue\": {\n" |
| 169 " \"value\": 2048\n" | 144 " \"value\": 2048\n" |
| 170 " },\n" | 145 " },\n" |
| 171 " \"repeatedMessageValue\": [\n" | 146 " \"repeatedMessageValue\": [\n" |
| 172 " {\"value\": 40}, {\"value\": 96}\n" | 147 " {\"value\": 40}, {\"value\": 96}\n" |
| 173 " ]\n" | 148 " ]\n" |
| 174 "}\n"; | 149 "}\n"; |
| 175 JsonParseOptions options; | |
| 176 TestMessage m; | 150 TestMessage m; |
| 177 ASSERT_TRUE(FromJson(input, &m, options)); | 151 ASSERT_TRUE(FromJson(input, &m)); |
| 178 EXPECT_EQ(1024, m.int32_value()); | 152 EXPECT_EQ(1024, m.int32_value()); |
| 179 ASSERT_EQ(2, m.repeated_int32_value_size()); | 153 ASSERT_EQ(2, m.repeated_int32_value_size()); |
| 180 EXPECT_EQ(1, m.repeated_int32_value(0)); | 154 EXPECT_EQ(1, m.repeated_int32_value(0)); |
| 181 EXPECT_EQ(2, m.repeated_int32_value(1)); | 155 EXPECT_EQ(2, m.repeated_int32_value(1)); |
| 182 EXPECT_EQ(2048, m.message_value().value()); | 156 EXPECT_EQ(2048, m.message_value().value()); |
| 183 ASSERT_EQ(2, m.repeated_message_value_size()); | 157 ASSERT_EQ(2, m.repeated_message_value_size()); |
| 184 EXPECT_EQ(40, m.repeated_message_value(0).value()); | 158 EXPECT_EQ(40, m.repeated_message_value(0).value()); |
| 185 EXPECT_EQ(96, m.repeated_message_value(1).value()); | 159 EXPECT_EQ(96, m.repeated_message_value(1).value()); |
| 186 } | 160 } |
| 187 | 161 |
| 188 TEST_F(JsonUtilTest, ParseMap) { | 162 TEST_F(JsonUtilTest, ParseMap) { |
| 189 TestMap message; | 163 TestMap message; |
| 190 (*message.mutable_string_map())["hello"] = 1234; | 164 (*message.mutable_string_map())["hello"] = 1234; |
| 191 JsonPrintOptions print_options; | 165 JsonOptions options; |
| 192 JsonParseOptions parse_options; | 166 EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, options)); |
| 193 EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, print_options)); | |
| 194 TestMap other; | 167 TestMap other; |
| 195 ASSERT_TRUE(FromJson(ToJson(message, print_options), &other, parse_options)); | 168 ASSERT_TRUE(FromJson(ToJson(message, options), &other)); |
| 196 EXPECT_EQ(message.DebugString(), other.DebugString()); | 169 EXPECT_EQ(message.DebugString(), other.DebugString()); |
| 197 } | 170 } |
| 198 | 171 |
| 199 TEST_F(JsonUtilTest, TestParseIgnoreUnknownFields) { | |
| 200 TestMessage m; | |
| 201 JsonParseOptions options; | |
| 202 options.ignore_unknown_fields = true; | |
| 203 EXPECT_TRUE(FromJson("{\"unknownName\":0}", &m, options)); | |
| 204 } | |
| 205 | |
| 206 TEST_F(JsonUtilTest, TestParseErrors) { | 172 TEST_F(JsonUtilTest, TestParseErrors) { |
| 207 TestMessage m; | 173 TestMessage m; |
| 208 JsonParseOptions options; | 174 JsonOptions options; |
| 209 // Parsing should fail if the field name can not be recognized. | 175 // Parsing should fail if the field name can not be recognized. |
| 210 EXPECT_FALSE(FromJson("{\"unknownName\":0}", &m, options)); | 176 EXPECT_FALSE(FromJson("{\"unknownName\":0}", &m)); |
| 211 // Parsing should fail if the value is invalid. | 177 // Parsing should fail if the value is invalid. |
| 212 EXPECT_FALSE(FromJson("{\"int32Value\":2147483648}", &m, options)); | 178 EXPECT_FALSE(FromJson("{\"int32Value\":2147483648}", &m)); |
| 213 } | 179 } |
| 214 | 180 |
| 215 TEST_F(JsonUtilTest, TestDynamicMessage) { | 181 typedef pair<char*, int> Segment; |
| 216 // Some random message but good enough to test the wrapper functions. | |
| 217 string input = | |
| 218 "{\n" | |
| 219 " \"int32Value\": 1024,\n" | |
| 220 " \"repeatedInt32Value\": [1, 2],\n" | |
| 221 " \"messageValue\": {\n" | |
| 222 " \"value\": 2048\n" | |
| 223 " },\n" | |
| 224 " \"repeatedMessageValue\": [\n" | |
| 225 " {\"value\": 40}, {\"value\": 96}\n" | |
| 226 " ]\n" | |
| 227 "}\n"; | |
| 228 | |
| 229 // Create a new DescriptorPool with the same protos as the generated one. | |
| 230 DescriptorPoolDatabase database(*DescriptorPool::generated_pool()); | |
| 231 DescriptorPool pool(&database); | |
| 232 // A dynamic version of the test proto. | |
| 233 DynamicMessageFactory factory; | |
| 234 google::protobuf::scoped_ptr<Message> message(factory.GetPrototype( | |
| 235 pool.FindMessageTypeByName("proto3.TestMessage"))->New()); | |
| 236 EXPECT_TRUE(FromJson(input, message.get())); | |
| 237 | |
| 238 // Convert to generated message for easy inspection. | |
| 239 TestMessage generated; | |
| 240 EXPECT_TRUE(generated.ParseFromString(message->SerializeAsString())); | |
| 241 EXPECT_EQ(1024, generated.int32_value()); | |
| 242 ASSERT_EQ(2, generated.repeated_int32_value_size()); | |
| 243 EXPECT_EQ(1, generated.repeated_int32_value(0)); | |
| 244 EXPECT_EQ(2, generated.repeated_int32_value(1)); | |
| 245 EXPECT_EQ(2048, generated.message_value().value()); | |
| 246 ASSERT_EQ(2, generated.repeated_message_value_size()); | |
| 247 EXPECT_EQ(40, generated.repeated_message_value(0).value()); | |
| 248 EXPECT_EQ(96, generated.repeated_message_value(1).value()); | |
| 249 | |
| 250 JsonOptions options; | |
| 251 EXPECT_EQ(ToJson(generated, options), ToJson(*message, options)); | |
| 252 } | |
| 253 | |
| 254 typedef std::pair<char*, int> Segment; | |
| 255 // A ZeroCopyOutputStream that writes to multiple buffers. | 182 // A ZeroCopyOutputStream that writes to multiple buffers. |
| 256 class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { | 183 class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { |
| 257 public: | 184 public: |
| 258 explicit SegmentedZeroCopyOutputStream(std::list<Segment> segments) | 185 explicit SegmentedZeroCopyOutputStream(list<Segment> segments) |
| 259 : segments_(segments), last_segment_(static_cast<char*>(NULL), 0), byte_co
unt_(0) {} | 186 : segments_(segments), last_segment_(static_cast<char*>(NULL), 0), byte_co
unt_(0) {} |
| 260 | 187 |
| 261 virtual bool Next(void** buffer, int* length) { | 188 virtual bool Next(void** buffer, int* length) { |
| 262 if (segments_.empty()) { | 189 if (segments_.empty()) { |
| 263 return false; | 190 return false; |
| 264 } | 191 } |
| 265 last_segment_ = segments_.front(); | 192 last_segment_ = segments_.front(); |
| 266 segments_.pop_front(); | 193 segments_.pop_front(); |
| 267 *buffer = last_segment_.first; | 194 *buffer = last_segment_.first; |
| 268 *length = last_segment_.second; | 195 *length = last_segment_.second; |
| 269 byte_count_ += *length; | 196 byte_count_ += *length; |
| 270 return true; | 197 return true; |
| 271 } | 198 } |
| 272 | 199 |
| 273 virtual void BackUp(int length) { | 200 virtual void BackUp(int length) { |
| 274 GOOGLE_CHECK(length <= last_segment_.second); | 201 GOOGLE_CHECK(length <= last_segment_.second); |
| 275 segments_.push_front( | 202 segments_.push_front( |
| 276 Segment(last_segment_.first + last_segment_.second - length, length)); | 203 Segment(last_segment_.first + last_segment_.second - length, length)); |
| 277 last_segment_ = Segment(last_segment_.first, last_segment_.second - length); | 204 last_segment_ = Segment(last_segment_.first, last_segment_.second - length); |
| 278 byte_count_ -= length; | 205 byte_count_ -= length; |
| 279 } | 206 } |
| 280 | 207 |
| 281 virtual int64 ByteCount() const { return byte_count_; } | 208 virtual int64 ByteCount() const { return byte_count_; } |
| 282 | 209 |
| 283 private: | 210 private: |
| 284 std::list<Segment> segments_; | 211 list<Segment> segments_; |
| 285 Segment last_segment_; | 212 Segment last_segment_; |
| 286 int64 byte_count_; | 213 int64 byte_count_; |
| 287 }; | 214 }; |
| 288 | 215 |
| 289 // This test splits the output buffer and also the input data into multiple | 216 // This test splits the output buffer and also the input data into multiple |
| 290 // segments and checks that the implementation of ZeroCopyStreamByteSink | 217 // segments and checks that the implementation of ZeroCopyStreamByteSink |
| 291 // handles all possible cases correctly. | 218 // handles all possible cases correctly. |
| 292 TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { | 219 TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { |
| 293 static const int kOutputBufferLength = 10; | 220 static const int kOutputBufferLength = 10; |
| 294 // An exhaustive test takes too long, skip some combinations to make the test | 221 // An exhaustive test takes too long, skip some combinations to make the test |
| 295 // run faster. | 222 // run faster. |
| 296 static const int kSkippedPatternCount = 7; | 223 static const int kSkippedPatternCount = 7; |
| 297 | 224 |
| 298 char buffer[kOutputBufferLength]; | 225 char buffer[kOutputBufferLength]; |
| 299 for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1)); | 226 for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1)); |
| 300 split_pattern += kSkippedPatternCount) { | 227 split_pattern += kSkippedPatternCount) { |
| 301 // Split the buffer into small segments according to the split_pattern. | 228 // Split the buffer into small segments according to the split_pattern. |
| 302 std::list<Segment> segments; | 229 list<Segment> segments; |
| 303 int segment_start = 0; | 230 int segment_start = 0; |
| 304 for (int i = 0; i < kOutputBufferLength - 1; ++i) { | 231 for (int i = 0; i < kOutputBufferLength - 1; ++i) { |
| 305 if (split_pattern & (1 << i)) { | 232 if (split_pattern & (1 << i)) { |
| 306 segments.push_back( | 233 segments.push_back( |
| 307 Segment(buffer + segment_start, i - segment_start + 1)); | 234 Segment(buffer + segment_start, i - segment_start + 1)); |
| 308 segment_start = i + 1; | 235 segment_start = i + 1; |
| 309 } | 236 } |
| 310 } | 237 } |
| 311 segments.push_back( | 238 segments.push_back( |
| 312 Segment(buffer + segment_start, kOutputBufferLength - segment_start)); | 239 Segment(buffer + segment_start, kOutputBufferLength - segment_start)); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 373 EXPECT_EQ(input_data.substr(0, kOutputBufferLength), | 300 EXPECT_EQ(input_data.substr(0, kOutputBufferLength), |
| 374 string(buffer, kOutputBufferLength)); | 301 string(buffer, kOutputBufferLength)); |
| 375 } | 302 } |
| 376 } | 303 } |
| 377 } | 304 } |
| 378 | 305 |
| 379 } // namespace | 306 } // namespace |
| 380 } // namespace util | 307 } // namespace util |
| 381 } // namespace protobuf | 308 } // namespace protobuf |
| 382 } // namespace google | 309 } // namespace google |
| OLD | NEW |