| OLD | NEW |
| (Empty) |
| 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 #include <google/protobuf/util/internal/protostream_objectsource.h> | |
| 32 | |
| 33 #include <utility> | |
| 34 | |
| 35 #include <google/protobuf/stubs/casts.h> | |
| 36 #include <google/protobuf/stubs/common.h> | |
| 37 #include <google/protobuf/stubs/stringprintf.h> | |
| 38 #include <google/protobuf/stubs/time.h> | |
| 39 #include <google/protobuf/io/coded_stream.h> | |
| 40 #include <google/protobuf/io/zero_copy_stream_impl.h> | |
| 41 #include <google/protobuf/descriptor.h> | |
| 42 #include <google/protobuf/wire_format.h> | |
| 43 #include <google/protobuf/wire_format_lite.h> | |
| 44 #include <google/protobuf/util/internal/field_mask_utility.h> | |
| 45 #include <google/protobuf/util/internal/constants.h> | |
| 46 #include <google/protobuf/util/internal/utility.h> | |
| 47 #include <google/protobuf/stubs/strutil.h> | |
| 48 #include <google/protobuf/stubs/map_util.h> | |
| 49 #include <google/protobuf/stubs/status_macros.h> | |
| 50 | |
| 51 | |
| 52 namespace google { | |
| 53 namespace protobuf { | |
| 54 namespace util { | |
| 55 using util::Status; | |
| 56 using util::StatusOr; | |
| 57 namespace error { | |
| 58 using util::error::Code; | |
| 59 using util::error::INTERNAL; | |
| 60 } | |
| 61 namespace converter { | |
| 62 | |
| 63 using google::protobuf::Descriptor; | |
| 64 using google::protobuf::EnumValueDescriptor; | |
| 65 using google::protobuf::FieldDescriptor; | |
| 66 using google::protobuf::internal::WireFormat; | |
| 67 using google::protobuf::internal::WireFormatLite; | |
| 68 using util::Status; | |
| 69 using util::StatusOr; | |
| 70 | |
| 71 namespace { | |
| 72 // Finds a field with the given number. NULL if none found. | |
| 73 const google::protobuf::Field* FindFieldByNumber( | |
| 74 const google::protobuf::Type& type, int number); | |
| 75 | |
| 76 // Returns true if the field is packable. | |
| 77 bool IsPackable(const google::protobuf::Field& field); | |
| 78 | |
| 79 // Finds an enum value with the given number. NULL if none found. | |
| 80 const google::protobuf::EnumValue* FindEnumValueByNumber( | |
| 81 const google::protobuf::Enum& tech_enum, int number); | |
| 82 | |
| 83 // Utility function to format nanos. | |
| 84 const string FormatNanos(uint32 nanos); | |
| 85 } // namespace | |
| 86 | |
| 87 | |
| 88 ProtoStreamObjectSource::ProtoStreamObjectSource( | |
| 89 google::protobuf::io::CodedInputStream* stream, TypeResolver* type_resolver, | |
| 90 const google::protobuf::Type& type) | |
| 91 : stream_(stream), | |
| 92 typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), | |
| 93 own_typeinfo_(true), | |
| 94 type_(type) { | |
| 95 GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; | |
| 96 } | |
| 97 | |
| 98 ProtoStreamObjectSource::ProtoStreamObjectSource( | |
| 99 google::protobuf::io::CodedInputStream* stream, TypeInfo* typeinfo, | |
| 100 const google::protobuf::Type& type) | |
| 101 : stream_(stream), typeinfo_(typeinfo), own_typeinfo_(false), type_(type) { | |
| 102 GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; | |
| 103 } | |
| 104 | |
| 105 ProtoStreamObjectSource::~ProtoStreamObjectSource() { | |
| 106 if (own_typeinfo_) { | |
| 107 delete typeinfo_; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 Status ProtoStreamObjectSource::NamedWriteTo(StringPiece name, | |
| 112 ObjectWriter* ow) const { | |
| 113 return WriteMessage(type_, name, 0, true, ow); | |
| 114 } | |
| 115 | |
| 116 const google::protobuf::Field* ProtoStreamObjectSource::FindAndVerifyField( | |
| 117 const google::protobuf::Type& type, uint32 tag) const { | |
| 118 // Lookup the new field in the type by tag number. | |
| 119 const google::protobuf::Field* field = FindFieldByNumber(type, tag >> 3); | |
| 120 // Verify if the field corresponds to the wire type in tag. | |
| 121 // If there is any discrepancy, mark the field as not found. | |
| 122 if (field != NULL) { | |
| 123 WireFormatLite::WireType expected_type = | |
| 124 WireFormatLite::WireTypeForFieldType( | |
| 125 static_cast<WireFormatLite::FieldType>(field->kind())); | |
| 126 WireFormatLite::WireType actual_type = WireFormatLite::GetTagWireType(tag); | |
| 127 if (actual_type != expected_type && | |
| 128 (!IsPackable(*field) || | |
| 129 actual_type != WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) { | |
| 130 field = NULL; | |
| 131 } | |
| 132 } | |
| 133 return field; | |
| 134 } | |
| 135 | |
| 136 Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, | |
| 137 StringPiece name, | |
| 138 const uint32 end_tag, | |
| 139 bool include_start_and_end, | |
| 140 ObjectWriter* ow) const { | |
| 141 const TypeRenderer* type_renderer = FindTypeRenderer(type.name()); | |
| 142 if (type_renderer != NULL) { | |
| 143 return (*type_renderer)(this, type, name, ow); | |
| 144 } | |
| 145 | |
| 146 const google::protobuf::Field* field = NULL; | |
| 147 string field_name; | |
| 148 // last_tag set to dummy value that is different from tag. | |
| 149 uint32 tag = stream_->ReadTag(), last_tag = tag + 1; | |
| 150 | |
| 151 if (include_start_and_end) { | |
| 152 ow->StartObject(name); | |
| 153 } | |
| 154 while (tag != end_tag) { | |
| 155 if (tag != last_tag) { // Update field only if tag is changed. | |
| 156 last_tag = tag; | |
| 157 field = FindAndVerifyField(type, tag); | |
| 158 if (field != NULL) { | |
| 159 field_name = field->name(); | |
| 160 } | |
| 161 } | |
| 162 if (field == NULL) { | |
| 163 // If we didn't find a field, skip this unknown tag. | |
| 164 // TODO(wpoon): Check return boolean value. | |
| 165 WireFormatLite::SkipField(stream_, tag); | |
| 166 tag = stream_->ReadTag(); | |
| 167 continue; | |
| 168 } | |
| 169 | |
| 170 if (field->cardinality() == | |
| 171 google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { | |
| 172 if (IsMap(*field)) { | |
| 173 ow->StartObject(field_name); | |
| 174 ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow)); | |
| 175 ow->EndObject(); | |
| 176 } else { | |
| 177 ASSIGN_OR_RETURN(tag, RenderList(field, field_name, tag, ow)); | |
| 178 } | |
| 179 } else { | |
| 180 // Render the field. | |
| 181 RETURN_IF_ERROR(RenderField(field, field_name, ow)); | |
| 182 tag = stream_->ReadTag(); | |
| 183 } | |
| 184 } | |
| 185 if (include_start_and_end) { | |
| 186 ow->EndObject(); | |
| 187 } | |
| 188 return Status::OK; | |
| 189 } | |
| 190 | |
| 191 StatusOr<uint32> ProtoStreamObjectSource::RenderList( | |
| 192 const google::protobuf::Field* field, StringPiece name, uint32 list_tag, | |
| 193 ObjectWriter* ow) const { | |
| 194 uint32 tag_to_return = 0; | |
| 195 ow->StartList(name); | |
| 196 if (IsPackable(*field) && | |
| 197 list_tag == | |
| 198 WireFormatLite::MakeTag(field->number(), | |
| 199 WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) { | |
| 200 RETURN_IF_ERROR(RenderPacked(field, ow)); | |
| 201 // Since packed fields have a single tag, read another tag from stream to | |
| 202 // return. | |
| 203 tag_to_return = stream_->ReadTag(); | |
| 204 } else { | |
| 205 do { | |
| 206 RETURN_IF_ERROR(RenderField(field, "", ow)); | |
| 207 } while ((tag_to_return = stream_->ReadTag()) == list_tag); | |
| 208 } | |
| 209 ow->EndList(); | |
| 210 return tag_to_return; | |
| 211 } | |
| 212 | |
| 213 StatusOr<uint32> ProtoStreamObjectSource::RenderMap( | |
| 214 const google::protobuf::Field* field, StringPiece name, uint32 list_tag, | |
| 215 ObjectWriter* ow) const { | |
| 216 const google::protobuf::Type* field_type = | |
| 217 typeinfo_->GetType(field->type_url()); | |
| 218 uint32 tag_to_return = 0; | |
| 219 if (IsPackable(*field) && | |
| 220 list_tag == | |
| 221 WireFormatLite::MakeTag(field->number(), | |
| 222 WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) { | |
| 223 RETURN_IF_ERROR(RenderPackedMapEntry(field_type, ow)); | |
| 224 tag_to_return = stream_->ReadTag(); | |
| 225 } else { | |
| 226 do { | |
| 227 RETURN_IF_ERROR(RenderMapEntry(field_type, ow)); | |
| 228 } while ((tag_to_return = stream_->ReadTag()) == list_tag); | |
| 229 } | |
| 230 return tag_to_return; | |
| 231 } | |
| 232 | |
| 233 Status ProtoStreamObjectSource::RenderMapEntry( | |
| 234 const google::protobuf::Type* type, ObjectWriter* ow) const { | |
| 235 uint32 buffer32; | |
| 236 stream_->ReadVarint32(&buffer32); // message length | |
| 237 int old_limit = stream_->PushLimit(buffer32); | |
| 238 string map_key; | |
| 239 for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) { | |
| 240 const google::protobuf::Field* field = FindAndVerifyField(*type, tag); | |
| 241 if (field == NULL) { | |
| 242 WireFormatLite::SkipField(stream_, tag); | |
| 243 continue; | |
| 244 } | |
| 245 // Map field numbers are key = 1 and value = 2 | |
| 246 if (field->number() == 1) { | |
| 247 map_key = ReadFieldValueAsString(*field); | |
| 248 } else if (field->number() == 2) { | |
| 249 if (map_key.empty()) { | |
| 250 return Status(util::error::INTERNAL, "Map key must be non-empty"); | |
| 251 } | |
| 252 // Disable case normalization for map keys as they are just data. We | |
| 253 // retain them intact. | |
| 254 ow->DisableCaseNormalizationForNextKey(); | |
| 255 RETURN_IF_ERROR(RenderField(field, map_key, ow)); | |
| 256 } | |
| 257 } | |
| 258 stream_->PopLimit(old_limit); | |
| 259 | |
| 260 return Status::OK; | |
| 261 } | |
| 262 | |
| 263 Status ProtoStreamObjectSource::RenderPacked( | |
| 264 const google::protobuf::Field* field, ObjectWriter* ow) const { | |
| 265 uint32 length; | |
| 266 stream_->ReadVarint32(&length); | |
| 267 int old_limit = stream_->PushLimit(length); | |
| 268 while (stream_->BytesUntilLimit() > 0) { | |
| 269 RETURN_IF_ERROR(RenderField(field, StringPiece(), ow)); | |
| 270 } | |
| 271 stream_->PopLimit(old_limit); | |
| 272 return Status::OK; | |
| 273 } | |
| 274 | |
| 275 Status ProtoStreamObjectSource::RenderPackedMapEntry( | |
| 276 const google::protobuf::Type* type, ObjectWriter* ow) const { | |
| 277 uint32 length; | |
| 278 stream_->ReadVarint32(&length); | |
| 279 int old_limit = stream_->PushLimit(length); | |
| 280 while (stream_->BytesUntilLimit() > 0) { | |
| 281 RETURN_IF_ERROR(RenderMapEntry(type, ow)); | |
| 282 } | |
| 283 stream_->PopLimit(old_limit); | |
| 284 return Status::OK; | |
| 285 } | |
| 286 | |
| 287 Status ProtoStreamObjectSource::RenderTimestamp( | |
| 288 const ProtoStreamObjectSource* os, const google::protobuf::Type& type, | |
| 289 StringPiece field_name, ObjectWriter* ow) { | |
| 290 pair<int64, int32> p = os->ReadSecondsAndNanos(type); | |
| 291 int64 seconds = p.first; | |
| 292 int32 nanos = p.second; | |
| 293 if (seconds > kMaxSeconds || seconds < kMinSeconds) { | |
| 294 return Status( | |
| 295 util::error::INTERNAL, | |
| 296 StrCat("Timestamp seconds exceeds limit for field: ", field_name)); | |
| 297 } | |
| 298 | |
| 299 if (nanos < 0 || nanos >= kNanosPerSecond) { | |
| 300 return Status( | |
| 301 util::error::INTERNAL, | |
| 302 StrCat("Timestamp nanos exceeds limit for field: ", field_name)); | |
| 303 } | |
| 304 | |
| 305 ow->RenderString(field_name, | |
| 306 ::google::protobuf::internal::FormatTime(seconds, nanos)); | |
| 307 | |
| 308 return Status::OK; | |
| 309 } | |
| 310 | |
| 311 Status ProtoStreamObjectSource::RenderDuration( | |
| 312 const ProtoStreamObjectSource* os, const google::protobuf::Type& type, | |
| 313 StringPiece field_name, ObjectWriter* ow) { | |
| 314 pair<int64, int32> p = os->ReadSecondsAndNanos(type); | |
| 315 int64 seconds = p.first; | |
| 316 int32 nanos = p.second; | |
| 317 if (seconds > kMaxSeconds || seconds < kMinSeconds) { | |
| 318 return Status( | |
| 319 util::error::INTERNAL, | |
| 320 StrCat("Duration seconds exceeds limit for field: ", field_name)); | |
| 321 } | |
| 322 | |
| 323 if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { | |
| 324 return Status( | |
| 325 util::error::INTERNAL, | |
| 326 StrCat("Duration nanos exceeds limit for field: ", field_name)); | |
| 327 } | |
| 328 | |
| 329 string sign = ""; | |
| 330 if (seconds < 0) { | |
| 331 if (nanos > 0) { | |
| 332 return Status(util::error::INTERNAL, | |
| 333 StrCat( | |
| 334 "Duration nanos is non-negative, but seconds is " | |
| 335 "negative for field: ", | |
| 336 field_name)); | |
| 337 } | |
| 338 sign = "-"; | |
| 339 seconds = -seconds; | |
| 340 nanos = -nanos; | |
| 341 } else if (seconds == 0 && nanos < 0) { | |
| 342 sign = "-"; | |
| 343 nanos = -nanos; | |
| 344 } | |
| 345 string formatted_duration = StringPrintf("%s%lld%ss", sign.c_str(), seconds, | |
| 346 FormatNanos(nanos).c_str()); | |
| 347 ow->RenderString(field_name, formatted_duration); | |
| 348 return Status::OK; | |
| 349 } | |
| 350 | |
| 351 Status ProtoStreamObjectSource::RenderDouble(const ProtoStreamObjectSource* os, | |
| 352 const google::protobuf::Type& type, | |
| 353 StringPiece field_name, | |
| 354 ObjectWriter* ow) { | |
| 355 uint32 tag = os->stream_->ReadTag(); | |
| 356 uint64 buffer64 = 0; // default value of Double wrapper value | |
| 357 if (tag != 0) { | |
| 358 os->stream_->ReadLittleEndian64(&buffer64); | |
| 359 os->stream_->ReadTag(); | |
| 360 } | |
| 361 ow->RenderDouble(field_name, bit_cast<double>(buffer64)); | |
| 362 return Status::OK; | |
| 363 } | |
| 364 | |
| 365 Status ProtoStreamObjectSource::RenderFloat(const ProtoStreamObjectSource* os, | |
| 366 const google::protobuf::Type& type, | |
| 367 StringPiece field_name, | |
| 368 ObjectWriter* ow) { | |
| 369 uint32 tag = os->stream_->ReadTag(); | |
| 370 uint32 buffer32 = 0; // default value of Float wrapper value | |
| 371 if (tag != 0) { | |
| 372 os->stream_->ReadLittleEndian32(&buffer32); | |
| 373 os->stream_->ReadTag(); | |
| 374 } | |
| 375 ow->RenderFloat(field_name, bit_cast<float>(buffer32)); | |
| 376 return Status::OK; | |
| 377 } | |
| 378 | |
| 379 Status ProtoStreamObjectSource::RenderInt64(const ProtoStreamObjectSource* os, | |
| 380 const google::protobuf::Type& type, | |
| 381 StringPiece field_name, | |
| 382 ObjectWriter* ow) { | |
| 383 uint32 tag = os->stream_->ReadTag(); | |
| 384 uint64 buffer64 = 0; // default value of Int64 wrapper value | |
| 385 if (tag != 0) { | |
| 386 os->stream_->ReadVarint64(&buffer64); | |
| 387 os->stream_->ReadTag(); | |
| 388 } | |
| 389 ow->RenderInt64(field_name, bit_cast<int64>(buffer64)); | |
| 390 return Status::OK; | |
| 391 } | |
| 392 | |
| 393 Status ProtoStreamObjectSource::RenderUInt64(const ProtoStreamObjectSource* os, | |
| 394 const google::protobuf::Type& type, | |
| 395 StringPiece field_name, | |
| 396 ObjectWriter* ow) { | |
| 397 uint32 tag = os->stream_->ReadTag(); | |
| 398 uint64 buffer64 = 0; // default value of UInt64 wrapper value | |
| 399 if (tag != 0) { | |
| 400 os->stream_->ReadVarint64(&buffer64); | |
| 401 os->stream_->ReadTag(); | |
| 402 } | |
| 403 ow->RenderUint64(field_name, bit_cast<uint64>(buffer64)); | |
| 404 return Status::OK; | |
| 405 } | |
| 406 | |
| 407 Status ProtoStreamObjectSource::RenderInt32(const ProtoStreamObjectSource* os, | |
| 408 const google::protobuf::Type& type, | |
| 409 StringPiece field_name, | |
| 410 ObjectWriter* ow) { | |
| 411 uint32 tag = os->stream_->ReadTag(); | |
| 412 uint32 buffer32 = 0; // default value of Int32 wrapper value | |
| 413 if (tag != 0) { | |
| 414 os->stream_->ReadVarint32(&buffer32); | |
| 415 os->stream_->ReadTag(); | |
| 416 } | |
| 417 ow->RenderInt32(field_name, bit_cast<int32>(buffer32)); | |
| 418 return Status::OK; | |
| 419 } | |
| 420 | |
| 421 Status ProtoStreamObjectSource::RenderUInt32(const ProtoStreamObjectSource* os, | |
| 422 const google::protobuf::Type& type, | |
| 423 StringPiece field_name, | |
| 424 ObjectWriter* ow) { | |
| 425 uint32 tag = os->stream_->ReadTag(); | |
| 426 uint32 buffer32 = 0; // default value of UInt32 wrapper value | |
| 427 if (tag != 0) { | |
| 428 os->stream_->ReadVarint32(&buffer32); | |
| 429 os->stream_->ReadTag(); | |
| 430 } | |
| 431 ow->RenderUint32(field_name, bit_cast<uint32>(buffer32)); | |
| 432 return Status::OK; | |
| 433 } | |
| 434 | |
| 435 Status ProtoStreamObjectSource::RenderBool(const ProtoStreamObjectSource* os, | |
| 436 const google::protobuf::Type& type, | |
| 437 StringPiece field_name, | |
| 438 ObjectWriter* ow) { | |
| 439 uint32 tag = os->stream_->ReadTag(); | |
| 440 uint64 buffer64 = 0; // results in 'false' value as default, which is the | |
| 441 // default value of Bool wrapper | |
| 442 if (tag != 0) { | |
| 443 os->stream_->ReadVarint64(&buffer64); | |
| 444 os->stream_->ReadTag(); | |
| 445 } | |
| 446 ow->RenderBool(field_name, buffer64 != 0); | |
| 447 return Status::OK; | |
| 448 } | |
| 449 | |
| 450 Status ProtoStreamObjectSource::RenderString(const ProtoStreamObjectSource* os, | |
| 451 const google::protobuf::Type& type, | |
| 452 StringPiece field_name, | |
| 453 ObjectWriter* ow) { | |
| 454 uint32 tag = os->stream_->ReadTag(); | |
| 455 uint32 buffer32; | |
| 456 string str; // default value of empty for String wrapper | |
| 457 if (tag != 0) { | |
| 458 os->stream_->ReadVarint32(&buffer32); // string size. | |
| 459 os->stream_->ReadString(&str, buffer32); | |
| 460 os->stream_->ReadTag(); | |
| 461 } | |
| 462 ow->RenderString(field_name, str); | |
| 463 return Status::OK; | |
| 464 } | |
| 465 | |
| 466 Status ProtoStreamObjectSource::RenderBytes(const ProtoStreamObjectSource* os, | |
| 467 const google::protobuf::Type& type, | |
| 468 StringPiece field_name, | |
| 469 ObjectWriter* ow) { | |
| 470 uint32 tag = os->stream_->ReadTag(); | |
| 471 uint32 buffer32; | |
| 472 string str; | |
| 473 if (tag != 0) { | |
| 474 os->stream_->ReadVarint32(&buffer32); | |
| 475 os->stream_->ReadString(&str, buffer32); | |
| 476 os->stream_->ReadTag(); | |
| 477 } | |
| 478 ow->RenderBytes(field_name, str); | |
| 479 return Status::OK; | |
| 480 } | |
| 481 | |
| 482 Status ProtoStreamObjectSource::RenderStruct(const ProtoStreamObjectSource* os, | |
| 483 const google::protobuf::Type& type, | |
| 484 StringPiece field_name, | |
| 485 ObjectWriter* ow) { | |
| 486 const google::protobuf::Field* field = NULL; | |
| 487 uint32 tag = os->stream_->ReadTag(); | |
| 488 ow->StartObject(field_name); | |
| 489 while (tag != 0) { | |
| 490 field = os->FindAndVerifyField(type, tag); | |
| 491 // google.protobuf.Struct has only one field that is a map. Hence we use | |
| 492 // RenderMap to render that field. | |
| 493 if (os->IsMap(*field)) { | |
| 494 ASSIGN_OR_RETURN(tag, os->RenderMap(field, field_name, tag, ow)); | |
| 495 } | |
| 496 } | |
| 497 ow->EndObject(); | |
| 498 return Status::OK; | |
| 499 } | |
| 500 | |
| 501 Status ProtoStreamObjectSource::RenderStructValue( | |
| 502 const ProtoStreamObjectSource* os, const google::protobuf::Type& type, | |
| 503 StringPiece field_name, ObjectWriter* ow) { | |
| 504 const google::protobuf::Field* field = NULL; | |
| 505 for (uint32 tag = os->stream_->ReadTag(); tag != 0; | |
| 506 tag = os->stream_->ReadTag()) { | |
| 507 field = os->FindAndVerifyField(type, tag); | |
| 508 if (field == NULL) { | |
| 509 WireFormatLite::SkipField(os->stream_, tag); | |
| 510 continue; | |
| 511 } | |
| 512 RETURN_IF_ERROR(os->RenderField(field, field_name, ow)); | |
| 513 } | |
| 514 return Status::OK; | |
| 515 } | |
| 516 | |
| 517 // TODO(skarvaje): Avoid code duplication of for loops and SkipField logic. | |
| 518 Status ProtoStreamObjectSource::RenderStructListValue( | |
| 519 const ProtoStreamObjectSource* os, const google::protobuf::Type& type, | |
| 520 StringPiece field_name, ObjectWriter* ow) { | |
| 521 uint32 tag = os->stream_->ReadTag(); | |
| 522 | |
| 523 // Render empty list when we find empty ListValue message. | |
| 524 if (tag == 0) { | |
| 525 ow->StartList(field_name); | |
| 526 ow->EndList(); | |
| 527 return Status::OK; | |
| 528 } | |
| 529 | |
| 530 while (tag != 0) { | |
| 531 const google::protobuf::Field* field = os->FindAndVerifyField(type, tag); | |
| 532 if (field == NULL) { | |
| 533 WireFormatLite::SkipField(os->stream_, tag); | |
| 534 tag = os->stream_->ReadTag(); | |
| 535 continue; | |
| 536 } | |
| 537 ASSIGN_OR_RETURN(tag, os->RenderList(field, field_name, tag, ow)); | |
| 538 } | |
| 539 return Status::OK; | |
| 540 } | |
| 541 | |
| 542 Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os, | |
| 543 const google::protobuf::Type& type, | |
| 544 StringPiece field_name, | |
| 545 ObjectWriter* ow) { | |
| 546 // An Any is of the form { string type_url = 1; bytes value = 2; } | |
| 547 uint32 tag; | |
| 548 string type_url; | |
| 549 string value; | |
| 550 | |
| 551 // First read out the type_url and value from the proto stream | |
| 552 for (tag = os->stream_->ReadTag(); tag != 0; tag = os->stream_->ReadTag()) { | |
| 553 const google::protobuf::Field* field = os->FindAndVerifyField(type, tag); | |
| 554 if (field == NULL) { | |
| 555 WireFormatLite::SkipField(os->stream_, tag); | |
| 556 continue; | |
| 557 } | |
| 558 // 'type_url' has field number of 1 and 'value' has field number 2 | |
| 559 // //google/protobuf/any.proto | |
| 560 if (field->number() == 1) { | |
| 561 // read type_url | |
| 562 uint32 type_url_size; | |
| 563 os->stream_->ReadVarint32(&type_url_size); | |
| 564 os->stream_->ReadString(&type_url, type_url_size); | |
| 565 } else if (field->number() == 2) { | |
| 566 // read value | |
| 567 uint32 value_size; | |
| 568 os->stream_->ReadVarint32(&value_size); | |
| 569 os->stream_->ReadString(&value, value_size); | |
| 570 } | |
| 571 } | |
| 572 | |
| 573 // If there is no value, we don't lookup the type, we just output it (if | |
| 574 // present). If both type and value are empty we output an empty object. | |
| 575 if (value.empty()) { | |
| 576 ow->StartObject(field_name); | |
| 577 if (!type_url.empty()) { | |
| 578 ow->RenderString("@type", type_url); | |
| 579 } | |
| 580 ow->EndObject(); | |
| 581 return util::Status::OK; | |
| 582 } | |
| 583 | |
| 584 // If there is a value but no type, we cannot render it, so report an error. | |
| 585 if (type_url.empty()) { | |
| 586 // TODO(sven): Add an external message once those are ready. | |
| 587 return util::Status(util::error::INTERNAL, | |
| 588 "Invalid Any, the type_url is missing."); | |
| 589 } | |
| 590 | |
| 591 util::StatusOr<const google::protobuf::Type*> resolved_type = | |
| 592 os->typeinfo_->ResolveTypeUrl(type_url); | |
| 593 | |
| 594 if (!resolved_type.ok()) { | |
| 595 // Convert into an internal error, since this means the backend gave us | |
| 596 // an invalid response (missing or invalid type information). | |
| 597 return util::Status(util::error::INTERNAL, | |
| 598 resolved_type.status().error_message()); | |
| 599 } | |
| 600 // nested_type cannot be null at this time. | |
| 601 const google::protobuf::Type* nested_type = resolved_type.ValueOrDie(); | |
| 602 | |
| 603 // We know the type so we can render it. Recursively parse the nested stream | |
| 604 // using a nested ProtoStreamObjectSource using our nested type information. | |
| 605 google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.si
ze()); | |
| 606 google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream); | |
| 607 ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type); | |
| 608 | |
| 609 // We manually call start and end object here so we can inject the @type. | |
| 610 ow->StartObject(field_name); | |
| 611 ow->RenderString("@type", type_url); | |
| 612 util::Status result = | |
| 613 nested_os.WriteMessage(nested_os.type_, "value", 0, false, ow); | |
| 614 ow->EndObject(); | |
| 615 return result; | |
| 616 } | |
| 617 | |
| 618 Status ProtoStreamObjectSource::RenderFieldMask( | |
| 619 const ProtoStreamObjectSource* os, const google::protobuf::Type& type, | |
| 620 StringPiece field_name, ObjectWriter* ow) { | |
| 621 string combined; | |
| 622 uint32 buffer32; | |
| 623 uint32 paths_field_tag = 0; | |
| 624 for (uint32 tag = os->stream_->ReadTag(); tag != 0; | |
| 625 tag = os->stream_->ReadTag()) { | |
| 626 if (paths_field_tag == 0) { | |
| 627 const google::protobuf::Field* field = os->FindAndVerifyField(type, tag); | |
| 628 if (field != NULL && field->number() == 1 && | |
| 629 field->name() == "paths") { | |
| 630 paths_field_tag = tag; | |
| 631 } | |
| 632 } | |
| 633 if (paths_field_tag != tag) { | |
| 634 return util::Status(util::error::INTERNAL, | |
| 635 "Invalid FieldMask, unexpected field."); | |
| 636 } | |
| 637 string str; | |
| 638 os->stream_->ReadVarint32(&buffer32); // string size. | |
| 639 os->stream_->ReadString(&str, buffer32); | |
| 640 if (!combined.empty()) { | |
| 641 combined.append(","); | |
| 642 } | |
| 643 combined.append(ConvertFieldMaskPath(str, &ToCamelCase)); | |
| 644 } | |
| 645 ow->RenderString(field_name, combined); | |
| 646 return Status::OK; | |
| 647 } | |
| 648 | |
| 649 hash_map<string, ProtoStreamObjectSource::TypeRenderer>* | |
| 650 ProtoStreamObjectSource::CreateRendererMap() { | |
| 651 hash_map<string, ProtoStreamObjectSource::TypeRenderer>* result = | |
| 652 new hash_map<string, ProtoStreamObjectSource::TypeRenderer>(); | |
| 653 (*result)["google.protobuf.Timestamp"] = | |
| 654 &ProtoStreamObjectSource::RenderTimestamp; | |
| 655 (*result)["google.protobuf.Duration"] = | |
| 656 &ProtoStreamObjectSource::RenderDuration; | |
| 657 (*result)["google.protobuf.DoubleValue"] = | |
| 658 &ProtoStreamObjectSource::RenderDouble; | |
| 659 (*result)["google.protobuf.FloatValue"] = | |
| 660 &ProtoStreamObjectSource::RenderFloat; | |
| 661 (*result)["google.protobuf.Int64Value"] = | |
| 662 &ProtoStreamObjectSource::RenderInt64; | |
| 663 (*result)["google.protobuf.UInt64Value"] = | |
| 664 &ProtoStreamObjectSource::RenderUInt64; | |
| 665 (*result)["google.protobuf.Int32Value"] = | |
| 666 &ProtoStreamObjectSource::RenderInt32; | |
| 667 (*result)["google.protobuf.UInt32Value"] = | |
| 668 &ProtoStreamObjectSource::RenderUInt32; | |
| 669 (*result)["google.protobuf.BoolValue"] = &ProtoStreamObjectSource::RenderBool; | |
| 670 (*result)["google.protobuf.StringValue"] = | |
| 671 &ProtoStreamObjectSource::RenderString; | |
| 672 (*result)["google.protobuf.BytesValue"] = | |
| 673 &ProtoStreamObjectSource::RenderBytes; | |
| 674 (*result)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny; | |
| 675 (*result)["google.protobuf.Struct"] = &ProtoStreamObjectSource::RenderStruct; | |
| 676 (*result)["google.protobuf.Value"] = | |
| 677 &ProtoStreamObjectSource::RenderStructValue; | |
| 678 (*result)["google.protobuf.ListValue"] = | |
| 679 &ProtoStreamObjectSource::RenderStructListValue; | |
| 680 (*result)["google.protobuf.FieldMask"] = | |
| 681 &ProtoStreamObjectSource::RenderFieldMask; | |
| 682 return result; | |
| 683 } | |
| 684 | |
| 685 // static | |
| 686 ProtoStreamObjectSource::TypeRenderer* | |
| 687 ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) { | |
| 688 static hash_map<string, TypeRenderer>* renderers = CreateRendererMap(); | |
| 689 return FindOrNull(*renderers, type_url); | |
| 690 } | |
| 691 | |
| 692 Status ProtoStreamObjectSource::RenderField( | |
| 693 const google::protobuf::Field* field, StringPiece field_name, | |
| 694 ObjectWriter* ow) const { | |
| 695 switch (field->kind()) { | |
| 696 case google::protobuf::Field_Kind_TYPE_BOOL: { | |
| 697 uint64 buffer64; | |
| 698 stream_->ReadVarint64(&buffer64); | |
| 699 ow->RenderBool(field_name, buffer64 != 0); | |
| 700 break; | |
| 701 } | |
| 702 case google::protobuf::Field_Kind_TYPE_INT32: { | |
| 703 uint32 buffer32; | |
| 704 stream_->ReadVarint32(&buffer32); | |
| 705 ow->RenderInt32(field_name, bit_cast<int32>(buffer32)); | |
| 706 break; | |
| 707 } | |
| 708 case google::protobuf::Field_Kind_TYPE_INT64: { | |
| 709 uint64 buffer64; | |
| 710 stream_->ReadVarint64(&buffer64); | |
| 711 ow->RenderInt64(field_name, bit_cast<int64>(buffer64)); | |
| 712 break; | |
| 713 } | |
| 714 case google::protobuf::Field_Kind_TYPE_UINT32: { | |
| 715 uint32 buffer32; | |
| 716 stream_->ReadVarint32(&buffer32); | |
| 717 ow->RenderUint32(field_name, bit_cast<uint32>(buffer32)); | |
| 718 break; | |
| 719 } | |
| 720 case google::protobuf::Field_Kind_TYPE_UINT64: { | |
| 721 uint64 buffer64; | |
| 722 stream_->ReadVarint64(&buffer64); | |
| 723 ow->RenderUint64(field_name, bit_cast<uint64>(buffer64)); | |
| 724 break; | |
| 725 } | |
| 726 case google::protobuf::Field_Kind_TYPE_SINT32: { | |
| 727 uint32 buffer32; | |
| 728 stream_->ReadVarint32(&buffer32); | |
| 729 ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32)); | |
| 730 break; | |
| 731 } | |
| 732 case google::protobuf::Field_Kind_TYPE_SINT64: { | |
| 733 uint64 buffer64; | |
| 734 stream_->ReadVarint64(&buffer64); | |
| 735 ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64)); | |
| 736 break; | |
| 737 } | |
| 738 case google::protobuf::Field_Kind_TYPE_SFIXED32: { | |
| 739 uint32 buffer32; | |
| 740 stream_->ReadLittleEndian32(&buffer32); | |
| 741 ow->RenderInt32(field_name, bit_cast<int32>(buffer32)); | |
| 742 break; | |
| 743 } | |
| 744 case google::protobuf::Field_Kind_TYPE_SFIXED64: { | |
| 745 uint64 buffer64; | |
| 746 stream_->ReadLittleEndian64(&buffer64); | |
| 747 ow->RenderInt64(field_name, bit_cast<int64>(buffer64)); | |
| 748 break; | |
| 749 } | |
| 750 case google::protobuf::Field_Kind_TYPE_FIXED32: { | |
| 751 uint32 buffer32; | |
| 752 stream_->ReadLittleEndian32(&buffer32); | |
| 753 ow->RenderUint32(field_name, bit_cast<uint32>(buffer32)); | |
| 754 break; | |
| 755 } | |
| 756 case google::protobuf::Field_Kind_TYPE_FIXED64: { | |
| 757 uint64 buffer64; | |
| 758 stream_->ReadLittleEndian64(&buffer64); | |
| 759 ow->RenderUint64(field_name, bit_cast<uint64>(buffer64)); | |
| 760 break; | |
| 761 } | |
| 762 case google::protobuf::Field_Kind_TYPE_FLOAT: { | |
| 763 uint32 buffer32; | |
| 764 stream_->ReadLittleEndian32(&buffer32); | |
| 765 ow->RenderFloat(field_name, bit_cast<float>(buffer32)); | |
| 766 break; | |
| 767 } | |
| 768 case google::protobuf::Field_Kind_TYPE_DOUBLE: { | |
| 769 uint64 buffer64; | |
| 770 stream_->ReadLittleEndian64(&buffer64); | |
| 771 ow->RenderDouble(field_name, bit_cast<double>(buffer64)); | |
| 772 break; | |
| 773 } | |
| 774 case google::protobuf::Field_Kind_TYPE_ENUM: { | |
| 775 uint32 buffer32; | |
| 776 stream_->ReadVarint32(&buffer32); | |
| 777 | |
| 778 // If the field represents an explicit NULL value, render null. | |
| 779 if (field->type_url() == kStructNullValueTypeUrl) { | |
| 780 ow->RenderNull(field_name); | |
| 781 break; | |
| 782 } | |
| 783 | |
| 784 // Get the nested enum type for this field. | |
| 785 // TODO(skarvaje): Avoid string manipulation. Find ways to speed this | |
| 786 // up. | |
| 787 const google::protobuf::Enum* en = typeinfo_->GetEnum(field->type_url()); | |
| 788 // Lookup the name of the enum, and render that. Skips unknown enums. | |
| 789 if (en != NULL) { | |
| 790 const google::protobuf::EnumValue* enum_value = | |
| 791 FindEnumValueByNumber(*en, buffer32); | |
| 792 if (enum_value != NULL) { | |
| 793 ow->RenderString(field_name, enum_value->name()); | |
| 794 } | |
| 795 } else { | |
| 796 GOOGLE_LOG(INFO) << "Unkown enum skipped: " << field->type_url(); | |
| 797 } | |
| 798 break; | |
| 799 } | |
| 800 case google::protobuf::Field_Kind_TYPE_STRING: { | |
| 801 uint32 buffer32; | |
| 802 string str; | |
| 803 stream_->ReadVarint32(&buffer32); // string size. | |
| 804 stream_->ReadString(&str, buffer32); | |
| 805 ow->RenderString(field_name, str); | |
| 806 break; | |
| 807 } | |
| 808 case google::protobuf::Field_Kind_TYPE_BYTES: { | |
| 809 uint32 buffer32; | |
| 810 stream_->ReadVarint32(&buffer32); // bytes size. | |
| 811 string value; | |
| 812 stream_->ReadString(&value, buffer32); | |
| 813 ow->RenderBytes(field_name, value); | |
| 814 break; | |
| 815 } | |
| 816 case google::protobuf::Field_Kind_TYPE_MESSAGE: { | |
| 817 uint32 buffer32; | |
| 818 stream_->ReadVarint32(&buffer32); // message length | |
| 819 int old_limit = stream_->PushLimit(buffer32); | |
| 820 // Get the nested message type for this field. | |
| 821 const google::protobuf::Type* type = | |
| 822 typeinfo_->GetType(field->type_url()); | |
| 823 if (type == NULL) { | |
| 824 return Status(util::error::INTERNAL, | |
| 825 StrCat("Invalid configuration. Could not find the type: ", | |
| 826 field->type_url())); | |
| 827 } | |
| 828 RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow)); | |
| 829 if (!stream_->ConsumedEntireMessage()) { | |
| 830 return Status(util::error::INVALID_ARGUMENT, | |
| 831 "Nested protocol message not parsed in its entirety."); | |
| 832 } | |
| 833 stream_->PopLimit(old_limit); | |
| 834 break; | |
| 835 } | |
| 836 default: | |
| 837 break; | |
| 838 } | |
| 839 return Status::OK; | |
| 840 } | |
| 841 | |
| 842 // TODO(skarvaje): Fix this to avoid code duplication. | |
| 843 const string ProtoStreamObjectSource::ReadFieldValueAsString( | |
| 844 const google::protobuf::Field& field) const { | |
| 845 string result; | |
| 846 switch (field.kind()) { | |
| 847 case google::protobuf::Field_Kind_TYPE_BOOL: { | |
| 848 uint64 buffer64; | |
| 849 stream_->ReadVarint64(&buffer64); | |
| 850 result = buffer64 != 0 ? "true" : "false"; | |
| 851 break; | |
| 852 } | |
| 853 case google::protobuf::Field_Kind_TYPE_INT32: { | |
| 854 uint32 buffer32; | |
| 855 stream_->ReadVarint32(&buffer32); | |
| 856 result = SimpleItoa(bit_cast<int32>(buffer32)); | |
| 857 break; | |
| 858 } | |
| 859 case google::protobuf::Field_Kind_TYPE_INT64: { | |
| 860 uint64 buffer64; | |
| 861 stream_->ReadVarint64(&buffer64); | |
| 862 result = SimpleItoa(bit_cast<int64>(buffer64)); | |
| 863 break; | |
| 864 } | |
| 865 case google::protobuf::Field_Kind_TYPE_UINT32: { | |
| 866 uint32 buffer32; | |
| 867 stream_->ReadVarint32(&buffer32); | |
| 868 result = SimpleItoa(bit_cast<uint32>(buffer32)); | |
| 869 break; | |
| 870 } | |
| 871 case google::protobuf::Field_Kind_TYPE_UINT64: { | |
| 872 uint64 buffer64; | |
| 873 stream_->ReadVarint64(&buffer64); | |
| 874 result = SimpleItoa(bit_cast<uint64>(buffer64)); | |
| 875 break; | |
| 876 } | |
| 877 case google::protobuf::Field_Kind_TYPE_SINT32: { | |
| 878 uint32 buffer32; | |
| 879 stream_->ReadVarint32(&buffer32); | |
| 880 result = SimpleItoa(WireFormatLite::ZigZagDecode32(buffer32)); | |
| 881 break; | |
| 882 } | |
| 883 case google::protobuf::Field_Kind_TYPE_SINT64: { | |
| 884 uint64 buffer64; | |
| 885 stream_->ReadVarint64(&buffer64); | |
| 886 result = SimpleItoa(WireFormatLite::ZigZagDecode64(buffer64)); | |
| 887 break; | |
| 888 } | |
| 889 case google::protobuf::Field_Kind_TYPE_SFIXED32: { | |
| 890 uint32 buffer32; | |
| 891 stream_->ReadLittleEndian32(&buffer32); | |
| 892 result = SimpleItoa(bit_cast<int32>(buffer32)); | |
| 893 break; | |
| 894 } | |
| 895 case google::protobuf::Field_Kind_TYPE_SFIXED64: { | |
| 896 uint64 buffer64; | |
| 897 stream_->ReadLittleEndian64(&buffer64); | |
| 898 result = SimpleItoa(bit_cast<int64>(buffer64)); | |
| 899 break; | |
| 900 } | |
| 901 case google::protobuf::Field_Kind_TYPE_FIXED32: { | |
| 902 uint32 buffer32; | |
| 903 stream_->ReadLittleEndian32(&buffer32); | |
| 904 result = SimpleItoa(bit_cast<uint32>(buffer32)); | |
| 905 break; | |
| 906 } | |
| 907 case google::protobuf::Field_Kind_TYPE_FIXED64: { | |
| 908 uint64 buffer64; | |
| 909 stream_->ReadLittleEndian64(&buffer64); | |
| 910 result = SimpleItoa(bit_cast<uint64>(buffer64)); | |
| 911 break; | |
| 912 } | |
| 913 case google::protobuf::Field_Kind_TYPE_FLOAT: { | |
| 914 uint32 buffer32; | |
| 915 stream_->ReadLittleEndian32(&buffer32); | |
| 916 result = SimpleFtoa(bit_cast<float>(buffer32)); | |
| 917 break; | |
| 918 } | |
| 919 case google::protobuf::Field_Kind_TYPE_DOUBLE: { | |
| 920 uint64 buffer64; | |
| 921 stream_->ReadLittleEndian64(&buffer64); | |
| 922 result = SimpleDtoa(bit_cast<double>(buffer64)); | |
| 923 break; | |
| 924 } | |
| 925 case google::protobuf::Field_Kind_TYPE_ENUM: { | |
| 926 uint32 buffer32; | |
| 927 stream_->ReadVarint32(&buffer32); | |
| 928 // Get the nested enum type for this field. | |
| 929 // TODO(skarvaje): Avoid string manipulation. Find ways to speed this | |
| 930 // up. | |
| 931 const google::protobuf::Enum* en = typeinfo_->GetEnum(field.type_url()); | |
| 932 // Lookup the name of the enum, and render that. Skips unknown enums. | |
| 933 if (en != NULL) { | |
| 934 const google::protobuf::EnumValue* enum_value = | |
| 935 FindEnumValueByNumber(*en, buffer32); | |
| 936 if (enum_value != NULL) { | |
| 937 result = enum_value->name(); | |
| 938 } | |
| 939 } | |
| 940 break; | |
| 941 } | |
| 942 case google::protobuf::Field_Kind_TYPE_STRING: { | |
| 943 uint32 buffer32; | |
| 944 stream_->ReadVarint32(&buffer32); // string size. | |
| 945 stream_->ReadString(&result, buffer32); | |
| 946 break; | |
| 947 } | |
| 948 case google::protobuf::Field_Kind_TYPE_BYTES: { | |
| 949 uint32 buffer32; | |
| 950 stream_->ReadVarint32(&buffer32); // bytes size. | |
| 951 stream_->ReadString(&result, buffer32); | |
| 952 break; | |
| 953 } | |
| 954 default: | |
| 955 break; | |
| 956 } | |
| 957 return result; | |
| 958 } | |
| 959 | |
| 960 // Field is a map if it is a repeated message and it has an option "map_type". | |
| 961 // TODO(skarvaje): Consider pre-computing the IsMap() into Field directly. | |
| 962 bool ProtoStreamObjectSource::IsMap( | |
| 963 const google::protobuf::Field& field) const { | |
| 964 const google::protobuf::Type* field_type = | |
| 965 typeinfo_->GetType(field.type_url()); | |
| 966 | |
| 967 // TODO(xiaofeng): Unify option names. | |
| 968 return field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE && | |
| 969 (GetBoolOptionOrDefault(field_type->options(), | |
| 970 "google.protobuf.MessageOptions.map_entry", fal
se) || | |
| 971 GetBoolOptionOrDefault(field_type->options(), "map_entry", false)); | |
| 972 } | |
| 973 | |
| 974 std::pair<int64, int32> ProtoStreamObjectSource::ReadSecondsAndNanos( | |
| 975 const google::protobuf::Type& type) const { | |
| 976 uint64 seconds = 0; | |
| 977 uint32 nanos = 0; | |
| 978 uint32 tag = 0; | |
| 979 int64 signed_seconds = 0; | |
| 980 int64 signed_nanos = 0; | |
| 981 | |
| 982 for (tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) { | |
| 983 const google::protobuf::Field* field = FindAndVerifyField(type, tag); | |
| 984 if (field == NULL) { | |
| 985 WireFormatLite::SkipField(stream_, tag); | |
| 986 continue; | |
| 987 } | |
| 988 // 'seconds' has field number of 1 and 'nanos' has field number 2 | |
| 989 // //google/protobuf/timestamp.proto & duration.proto | |
| 990 if (field->number() == 1) { | |
| 991 // read seconds | |
| 992 stream_->ReadVarint64(&seconds); | |
| 993 signed_seconds = bit_cast<int64>(seconds); | |
| 994 } else if (field->number() == 2) { | |
| 995 // read nanos | |
| 996 stream_->ReadVarint32(&nanos); | |
| 997 signed_nanos = bit_cast<int32>(nanos); | |
| 998 } | |
| 999 } | |
| 1000 return std::pair<int64, int32>(signed_seconds, signed_nanos); | |
| 1001 } | |
| 1002 | |
| 1003 namespace { | |
| 1004 // TODO(skarvaje): Speed this up by not doing a linear scan. | |
| 1005 const google::protobuf::Field* FindFieldByNumber( | |
| 1006 const google::protobuf::Type& type, int number) { | |
| 1007 for (int i = 0; i < type.fields_size(); ++i) { | |
| 1008 if (type.fields(i).number() == number) { | |
| 1009 return &type.fields(i); | |
| 1010 } | |
| 1011 } | |
| 1012 return NULL; | |
| 1013 } | |
| 1014 | |
| 1015 // TODO(skarvaje): Replace FieldDescriptor by implementing IsTypePackable() | |
| 1016 // using tech Field. | |
| 1017 bool IsPackable(const google::protobuf::Field& field) { | |
| 1018 return field.cardinality() == | |
| 1019 google::protobuf::Field_Cardinality_CARDINALITY_REPEATED && | |
| 1020 google::protobuf::FieldDescriptor::IsTypePackable( | |
| 1021 static_cast<google::protobuf::FieldDescriptor::Type>(field.kind()))
; | |
| 1022 } | |
| 1023 | |
| 1024 // TODO(skarvaje): Speed this up by not doing a linear scan. | |
| 1025 const google::protobuf::EnumValue* FindEnumValueByNumber( | |
| 1026 const google::protobuf::Enum& tech_enum, int number) { | |
| 1027 for (int i = 0; i < tech_enum.enumvalue_size(); ++i) { | |
| 1028 const google::protobuf::EnumValue& ev = tech_enum.enumvalue(i); | |
| 1029 if (ev.number() == number) { | |
| 1030 return &ev; | |
| 1031 } | |
| 1032 } | |
| 1033 return NULL; | |
| 1034 } | |
| 1035 | |
| 1036 // TODO(skarvaje): Look into optimizing this by not doing computation on | |
| 1037 // double. | |
| 1038 const string FormatNanos(uint32 nanos) { | |
| 1039 const char* format = | |
| 1040 (nanos % 1000 != 0) ? "%.9f" : (nanos % 1000000 != 0) ? "%.6f" : "%.3f"; | |
| 1041 string formatted = | |
| 1042 StringPrintf(format, static_cast<double>(nanos) / kNanosPerSecond); | |
| 1043 // remove the leading 0 before decimal. | |
| 1044 return formatted.substr(1); | |
| 1045 } | |
| 1046 } // namespace | |
| 1047 | |
| 1048 } // namespace converter | |
| 1049 } // namespace util | |
| 1050 } // namespace protobuf | |
| 1051 } // namespace google | |
| OLD | NEW |