|
25 | 25 | #include <utility>
|
26 | 26 |
|
27 | 27 | #include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h"
|
| 28 | +#include "Firestore/core/include/firebase/firestore/timestamp.h" |
28 | 29 | #include "Firestore/core/src/firebase/firestore/model/resource_path.h"
|
| 30 | +#include "Firestore/core/src/firebase/firestore/timestamp_internal.h" |
29 | 31 | #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
|
30 | 32 |
|
31 | 33 | namespace firebase {
|
32 | 34 | namespace firestore {
|
33 | 35 | namespace remote {
|
34 | 36 |
|
| 37 | +using firebase::Timestamp; |
| 38 | +using firebase::TimestampInternal; |
35 | 39 | using firebase::firestore::model::DatabaseId;
|
36 | 40 | using firebase::firestore::model::DocumentKey;
|
37 | 41 | using firebase::firestore::model::FieldValue;
|
@@ -95,6 +99,15 @@ class Writer {
|
95 | 99 | */
|
96 | 100 | void WriteTag(Tag tag);
|
97 | 101 |
|
| 102 | + /** |
| 103 | + * Writes a nanopb message to the output stream. |
| 104 | + * |
| 105 | + * This essentially wraps calls to nanopb's `pb_encode()` method. If we didn't |
| 106 | + * use `oneof`s in our protos, this would be the primary way of encoding |
| 107 | + * messages. |
| 108 | + */ |
| 109 | + void WriteNanopbMessage(const pb_field_t fields[], const void* src_struct); |
| 110 | + |
98 | 111 | void WriteSize(size_t size);
|
99 | 112 | void WriteNull();
|
100 | 113 | void WriteBool(bool bool_value);
|
@@ -179,6 +192,15 @@ class Reader {
|
179 | 192 | */
|
180 | 193 | Tag ReadTag();
|
181 | 194 |
|
| 195 | + /** |
| 196 | + * Reads a nanopb message from the input stream. |
| 197 | + * |
| 198 | + * This essentially wraps calls to nanopb's pb_decode() method. If we didn't |
| 199 | + * use `oneof`s in our protos, this would be the primary way of decoding |
| 200 | + * messages. |
| 201 | + */ |
| 202 | + void ReadNanopbMessage(const pb_field_t fields[], void* dest_struct); |
| 203 | + |
182 | 204 | void ReadNull();
|
183 | 205 | bool ReadBool();
|
184 | 206 | int64_t ReadInteger();
|
@@ -299,6 +321,23 @@ Tag Reader::ReadTag() {
|
299 | 321 | return tag;
|
300 | 322 | }
|
301 | 323 |
|
| 324 | +void Writer::WriteNanopbMessage(const pb_field_t fields[], |
| 325 | + const void* src_struct) { |
| 326 | + if (!status_.ok()) return; |
| 327 | + |
| 328 | + if (!pb_encode(&stream_, fields, src_struct)) { |
| 329 | + FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); |
| 330 | + } |
| 331 | +} |
| 332 | + |
| 333 | +void Reader::ReadNanopbMessage(const pb_field_t fields[], void* dest_struct) { |
| 334 | + if (!status_.ok()) return; |
| 335 | + |
| 336 | + if (!pb_decode(&stream_, fields, dest_struct)) { |
| 337 | + status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); |
| 338 | + } |
| 339 | +} |
| 340 | + |
302 | 341 | void Writer::WriteSize(size_t size) {
|
303 | 342 | return WriteVarint(size);
|
304 | 343 | }
|
@@ -415,6 +454,43 @@ std::string Reader::ReadString() {
|
415 | 454 | return result;
|
416 | 455 | }
|
417 | 456 |
|
| 457 | +void EncodeTimestamp(Writer* writer, const Timestamp& timestamp_value) { |
| 458 | + google_protobuf_Timestamp timestamp_proto = |
| 459 | + google_protobuf_Timestamp_init_zero; |
| 460 | + timestamp_proto.seconds = timestamp_value.seconds(); |
| 461 | + timestamp_proto.nanos = timestamp_value.nanoseconds(); |
| 462 | + writer->WriteNanopbMessage(google_protobuf_Timestamp_fields, |
| 463 | + ×tamp_proto); |
| 464 | +} |
| 465 | + |
| 466 | +Timestamp DecodeTimestamp(Reader* reader) { |
| 467 | + google_protobuf_Timestamp timestamp_proto = |
| 468 | + google_protobuf_Timestamp_init_zero; |
| 469 | + reader->ReadNanopbMessage(google_protobuf_Timestamp_fields, ×tamp_proto); |
| 470 | + |
| 471 | + // The Timestamp ctor will assert if we provide values outside the valid |
| 472 | + // range. However, since we're decoding, a single corrupt byte could cause |
| 473 | + // this to occur, so we'll verify the ranges before passing them in since we'd |
| 474 | + // rather not abort in these situations. |
| 475 | + if (timestamp_proto.seconds < TimestampInternal::Min().seconds()) { |
| 476 | + reader->set_status(Status( |
| 477 | + FirestoreErrorCode::DataLoss, |
| 478 | + "Invalid message: timestamp beyond the earliest supported date")); |
| 479 | + return {}; |
| 480 | + } else if (TimestampInternal::Max().seconds() < timestamp_proto.seconds) { |
| 481 | + reader->set_status( |
| 482 | + Status(FirestoreErrorCode::DataLoss, |
| 483 | + "Invalid message: timestamp behond the latest supported date")); |
| 484 | + return {}; |
| 485 | + } else if (timestamp_proto.nanos < 0 || timestamp_proto.nanos > 999999999) { |
| 486 | + reader->set_status(Status( |
| 487 | + FirestoreErrorCode::DataLoss, |
| 488 | + "Invalid message: timestamp nanos must be between 0 and 999999999")); |
| 489 | + return {}; |
| 490 | + } |
| 491 | + return Timestamp{timestamp_proto.seconds, timestamp_proto.nanos}; |
| 492 | +} |
| 493 | + |
418 | 494 | // Named '..Impl' so as to not conflict with Serializer::EncodeFieldValue.
|
419 | 495 | // TODO(rsgowman): Refactor to use a helper class that wraps the stream struct.
|
420 | 496 | // This will help with error handling, and should eliminate the issue of two
|
@@ -447,6 +523,14 @@ void EncodeFieldValueImpl(Writer* writer, const FieldValue& field_value) {
|
447 | 523 | writer->WriteString(field_value.string_value());
|
448 | 524 | break;
|
449 | 525 |
|
| 526 | + case FieldValue::Type::Timestamp: |
| 527 | + writer->WriteTag( |
| 528 | + {PB_WT_STRING, google_firestore_v1beta1_Value_timestamp_value_tag}); |
| 529 | + writer->WriteNestedMessage([&field_value](Writer* writer) { |
| 530 | + EncodeTimestamp(writer, field_value.timestamp_value()); |
| 531 | + }); |
| 532 | + break; |
| 533 | + |
450 | 534 | case FieldValue::Type::Object:
|
451 | 535 | writer->WriteTag(
|
452 | 536 | {PB_WT_STRING, google_firestore_v1beta1_Value_map_value_tag});
|
@@ -477,6 +561,7 @@ FieldValue DecodeFieldValueImpl(Reader* reader) {
|
477 | 561 | break;
|
478 | 562 |
|
479 | 563 | case google_firestore_v1beta1_Value_string_value_tag:
|
| 564 | + case google_firestore_v1beta1_Value_timestamp_value_tag: |
480 | 565 | case google_firestore_v1beta1_Value_map_value_tag:
|
481 | 566 | if (tag.wire_type != PB_WT_STRING) {
|
482 | 567 | reader->set_status(
|
@@ -517,6 +602,9 @@ FieldValue DecodeFieldValueImpl(Reader* reader) {
|
517 | 602 | return FieldValue::IntegerValue(reader->ReadInteger());
|
518 | 603 | case google_firestore_v1beta1_Value_string_value_tag:
|
519 | 604 | return FieldValue::StringValue(reader->ReadString());
|
| 605 | + case google_firestore_v1beta1_Value_timestamp_value_tag: |
| 606 | + return FieldValue::TimestampValue( |
| 607 | + reader->ReadNestedMessage<Timestamp>(DecodeTimestamp)); |
520 | 608 | case google_firestore_v1beta1_Value_map_value_tag:
|
521 | 609 | return FieldValue::ObjectValueFromMap(DecodeObject(reader));
|
522 | 610 |
|
|
0 commit comments