Skip to content

Commit 2ebb82d

Browse files
authored
LocalSerializer feature complete (#4046)
* LocalSerializer feature complete * Addressing comments 1 * Walk around nanopb boolean issue * Rename to SafeReadBoolean
1 parent 3942be5 commit 2ebb82d

File tree

7 files changed

+151
-83
lines changed

7 files changed

+151
-83
lines changed

Firestore/Example/App/Testing

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 0688b41b5c9ac80b6f8e17394680070926a65d63

Firestore/core/src/firebase/firestore/local/local_serializer.cc

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,19 @@ namespace {
4242

4343
using core::Query;
4444
using model::Document;
45+
using model::DocumentState;
46+
using model::FieldValue;
4547
using model::MaybeDocument;
4648
using model::Mutation;
4749
using model::MutationBatch;
4850
using model::NoDocument;
51+
using model::ObjectValue;
4952
using model::SnapshotVersion;
5053
using model::UnknownDocument;
5154
using nanopb::ByteString;
5255
using nanopb::CheckedSize;
5356
using nanopb::Reader;
57+
using nanopb::SafeReadBoolean;
5458
using nanopb::Writer;
5559
using remote::InvalidQuery;
5660
using remote::MakeArray;
@@ -64,34 +68,34 @@ firestore_client_MaybeDocument LocalSerializer::EncodeMaybeDocument(
6468
firestore_client_MaybeDocument result{};
6569

6670
switch (maybe_doc.type()) {
67-
case MaybeDocument::Type::Document:
71+
case MaybeDocument::Type::Document: {
6872
result.which_document_type = firestore_client_MaybeDocument_document_tag;
69-
result.document = EncodeDocument(static_cast<const Document&>(maybe_doc));
70-
// TODO(rsgowman): heldwriteacks:
71-
// result.has_committed_mutations = existing_doc.HasCommittedMutations();
73+
Document doc(maybe_doc);
74+
// TODO(wuandy): Check if `doc` already has a proto and use that if yes.
75+
result.document = EncodeDocument(doc);
76+
result.has_committed_mutations = doc.has_committed_mutations();
7277
return result;
78+
}
7379

74-
case MaybeDocument::Type::NoDocument:
80+
case MaybeDocument::Type::NoDocument: {
7581
result.which_document_type =
7682
firestore_client_MaybeDocument_no_document_tag;
77-
result.no_document =
78-
EncodeNoDocument(static_cast<const NoDocument&>(maybe_doc));
79-
// TODO(rsgowman): heldwriteacks:
80-
// result.has_committed_mutations = no_doc.HasCommittedMutations();
83+
NoDocument no_doc(maybe_doc);
84+
result.no_document = EncodeNoDocument(no_doc);
85+
result.has_committed_mutations = no_doc.has_committed_mutations();
8186
return result;
87+
}
8288

8389
case MaybeDocument::Type::UnknownDocument:
8490
result.which_document_type =
8591
firestore_client_MaybeDocument_unknown_document_tag;
8692
result.unknown_document =
87-
EncodeUnknownDocument(static_cast<const UnknownDocument&>(maybe_doc));
88-
// TODO(rsgowman): heldwriteacks:
89-
// result.has_committed_mutations = true;
93+
EncodeUnknownDocument(UnknownDocument(maybe_doc));
94+
result.has_committed_mutations = true;
9095
return result;
9196

9297
case MaybeDocument::Type::Invalid:
93-
// TODO(rsgowman): Error handling
94-
abort();
98+
HARD_FAIL("Unknown document type %s", maybe_doc.type());
9599
}
96100

97101
UNREACHABLE();
@@ -103,10 +107,12 @@ MaybeDocument LocalSerializer::DecodeMaybeDocument(
103107

104108
switch (proto.which_document_type) {
105109
case firestore_client_MaybeDocument_document_tag:
106-
return rpc_serializer_.DecodeDocument(reader, proto.document);
110+
return DecodeDocument(reader, proto.document,
111+
SafeReadBoolean(proto.has_committed_mutations));
107112

108113
case firestore_client_MaybeDocument_no_document_tag:
109-
return DecodeNoDocument(reader, proto.no_document);
114+
return DecodeNoDocument(reader, proto.no_document,
115+
SafeReadBoolean(proto.has_committed_mutations));
110116

111117
case firestore_client_MaybeDocument_unknown_document_tag:
112118
return DecodeUnknownDocument(reader, proto.unknown_document);
@@ -148,6 +154,23 @@ google_firestore_v1_Document LocalSerializer::EncodeDocument(
148154
return result;
149155
}
150156

157+
Document LocalSerializer::DecodeDocument(
158+
Reader* reader,
159+
const google_firestore_v1_Document& proto,
160+
bool has_committed_mutations) const {
161+
ObjectValue fields =
162+
rpc_serializer_.DecodeFields(reader, proto.fields_count, proto.fields);
163+
SnapshotVersion version =
164+
rpc_serializer_.DecodeVersion(reader, proto.update_time);
165+
166+
DocumentState state = has_committed_mutations
167+
? DocumentState::kCommittedMutations
168+
: DocumentState::kSynced;
169+
return Document(std::move(fields),
170+
rpc_serializer_.DecodeKey(reader, proto.name), version,
171+
state);
172+
}
173+
151174
firestore_client_NoDocument LocalSerializer::EncodeNoDocument(
152175
const NoDocument& no_doc) const {
153176
firestore_client_NoDocument result{};
@@ -159,15 +182,14 @@ firestore_client_NoDocument LocalSerializer::EncodeNoDocument(
159182
}
160183

161184
NoDocument LocalSerializer::DecodeNoDocument(
162-
Reader* reader, const firestore_client_NoDocument& proto) const {
185+
Reader* reader,
186+
const firestore_client_NoDocument& proto,
187+
bool has_committed_mutations) const {
163188
SnapshotVersion version =
164189
rpc_serializer_.DecodeVersion(reader, proto.read_time);
165190

166-
// TODO(rsgowman): Fix hardcoding of has_committed_mutations.
167-
// Instead, we should grab this from the proto (see other ports). However,
168-
// we'll defer until the nanopb-master gets merged to master.
169191
return NoDocument(rpc_serializer_.DecodeKey(reader, proto.name), version,
170-
/*has_committed_mutations=*/false);
192+
has_committed_mutations);
171193
}
172194

173195
firestore_client_UnknownDocument LocalSerializer::EncodeUnknownDocument(
@@ -203,13 +225,8 @@ firestore_client_Target LocalSerializer::EncodeQueryData(
203225

204226
const Query& query = query_data.query();
205227
if (query.IsDocumentQuery()) {
206-
// TODO(rsgowman): Implement. Probably like this (once EncodeDocumentsTarget
207-
// exists):
208-
/*
209-
result.which_target_type = firestore_client_Target_document_tag;
228+
result.which_target_type = firestore_client_Target_documents_tag;
210229
result.documents = rpc_serializer_.EncodeDocumentsTarget(query);
211-
*/
212-
abort();
213230
} else {
214231
result.which_target_type = firestore_client_Target_query_tag;
215232
result.query = rpc_serializer_.EncodeQueryTarget(query);
@@ -223,7 +240,6 @@ QueryData LocalSerializer::DecodeQueryData(
223240
if (!reader->status().ok()) return QueryData::Invalid();
224241

225242
model::TargetId target_id = proto.target_id;
226-
// TODO(rgowman): How to handle truncation of integer types?
227243
model::ListenSequenceNumber sequence_number =
228244
static_cast<model::ListenSequenceNumber>(
229245
proto.last_listen_sequence_number);
@@ -238,8 +254,8 @@ QueryData LocalSerializer::DecodeQueryData(
238254
break;
239255

240256
case firestore_client_Target_documents_tag:
241-
// TODO(rsgowman): Implement.
242-
abort();
257+
query = rpc_serializer_.DecodeDocumentsTarget(reader, proto.documents);
258+
break;
243259

244260
default:
245261
reader->Fail(
@@ -303,6 +319,16 @@ MutationBatch LocalSerializer::DecodeMutationBatch(
303319
std::move(mutations));
304320
}
305321

322+
google_protobuf_Timestamp LocalSerializer::EncodeVersion(
323+
const model::SnapshotVersion& version) const {
324+
return rpc_serializer_.EncodeVersion(version);
325+
}
326+
327+
model::SnapshotVersion LocalSerializer::DecodeVersion(
328+
nanopb::Reader* reader, const google_protobuf_Timestamp& proto) const {
329+
return rpc_serializer_.DecodeVersion(reader, proto);
330+
}
331+
306332
} // namespace local
307333
} // namespace firestore
308334
} // namespace firebase

Firestore/core/src/firebase/firestore/local/local_serializer.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ class LocalSerializer {
113113
model::MutationBatch DecodeMutationBatch(
114114
nanopb::Reader* reader, const firestore_client_WriteBatch& proto) const;
115115

116+
google_protobuf_Timestamp EncodeVersion(
117+
const model::SnapshotVersion& version) const;
118+
119+
model::SnapshotVersion DecodeVersion(
120+
nanopb::Reader* reader, const google_protobuf_Timestamp& proto) const;
121+
116122
private:
117123
/**
118124
* Encodes a Document for local storage. This differs from the v1 RPC
@@ -121,11 +127,16 @@ class LocalSerializer {
121127
*/
122128
google_firestore_v1_Document EncodeDocument(const model::Document& doc) const;
123129

130+
model::Document DecodeDocument(nanopb::Reader* reader,
131+
const google_firestore_v1_Document& proto,
132+
bool has_committed_mutations) const;
133+
124134
firestore_client_NoDocument EncodeNoDocument(
125135
const model::NoDocument& no_doc) const;
126136

127-
model::NoDocument DecodeNoDocument(
128-
nanopb::Reader* reader, const firestore_client_NoDocument& proto) const;
137+
model::NoDocument DecodeNoDocument(nanopb::Reader* reader,
138+
const firestore_client_NoDocument& proto,
139+
bool has_committed_mutations) const;
129140

130141
firestore_client_UnknownDocument EncodeUnknownDocument(
131142
const model::UnknownDocument& unknown_doc) const;

Firestore/core/src/firebase/firestore/nanopb/nanopb_util.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include "Firestore/core/src/firebase/firestore/nanopb/byte_string.h"
2828
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
2929
#include "Firestore/core/src/firebase/firestore/util/nullability.h"
30+
#include "absl/base/casts.h"
31+
#include "absl/memory/memory.h"
3032

3133
namespace firebase {
3234
namespace firestore {
@@ -88,6 +90,20 @@ inline std::vector<uint8_t> MakeVector(const ByteString& str) {
8890
return {str.begin(), str.end()};
8991
}
9092

93+
/**
94+
* Due to the nanopb implementation, nanopb_boolean could be an integer
95+
* other than 0 or 1, (such as 2). This leads to undefined behaviour when
96+
* it's read as a boolean. eg. on at least gcc, the value is treated as
97+
* both true *and* false. So we'll instead memcpy to an integer (via
98+
* absl::bit_cast) and compare with 0.
99+
*
100+
* Note that it is necessary to pass-by-reference here to get the original
101+
* value of `nanopb_boolean`.
102+
*/
103+
inline bool SafeReadBoolean(const bool& nanopb_boolean) {
104+
return absl::bit_cast<int8_t>(nanopb_boolean) != 0;
105+
}
106+
91107
#if __OBJC__
92108
inline ByteString MakeByteString(NSData* _Nullable value) {
93109
if (value == nil) return ByteString();

Firestore/core/src/firebase/firestore/remote/serializer.cc

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@
4949
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
5050
#include "Firestore/core/src/firebase/firestore/util/string_format.h"
5151
#include "absl/algorithm/container.h"
52-
#include "absl/base/casts.h"
53-
#include "absl/memory/memory.h"
5452

5553
namespace firebase {
5654
namespace firestore {
@@ -96,6 +94,7 @@ using nanopb::ByteString;
9694
using nanopb::CheckedSize;
9795
using nanopb::MakeStringView;
9896
using nanopb::Reader;
97+
using nanopb::SafeReadBoolean;
9998
using nanopb::Writer;
10099
using remote::WatchChange;
101100
using util::Status;
@@ -355,7 +354,7 @@ FieldValue::Map::value_type Serializer::DecodeFieldsEntry(
355354
return FieldValue::Map::value_type{std::move(key), std::move(value)};
356355
}
357356

358-
FieldValue::Map Serializer::DecodeFields(
357+
ObjectValue Serializer::DecodeFields(
359358
Reader* reader,
360359
size_t count,
361360
const google_firestore_v1_Document_FieldsEntry* fields) const {
@@ -365,7 +364,7 @@ FieldValue::Map Serializer::DecodeFields(
365364
result = result.insert(std::move(kv.first), std::move(kv.second));
366365
}
367366

368-
return result;
367+
return ObjectValue::FromMap(result);
369368
}
370369

371370
FieldValue::Map Serializer::DecodeMapValue(
@@ -392,13 +391,7 @@ FieldValue Serializer::DecodeFieldValue(
392391
return FieldValue::Null();
393392

394393
case google_firestore_v1_Value_boolean_value_tag: {
395-
// Due to the nanopb implementation, msg.boolean_value could be an integer
396-
// other than 0 or 1, (such as 2). This leads to undefined behaviour when
397-
// it's read as a boolean. eg. on at least gcc, the value is treated as
398-
// both true *and* false. So we'll instead memcpy to an integer (via
399-
// absl::bit_cast) and compare with 0.
400-
int bool_as_int = absl::bit_cast<int8_t>(msg.boolean_value);
401-
return FieldValue::FromBoolean(bool_as_int != 0);
394+
return FieldValue::FromBoolean(SafeReadBoolean(msg.boolean_value));
402395
}
403396

404397
case google_firestore_v1_Value_integer_value_tag:
@@ -551,16 +544,16 @@ Document Serializer::DecodeFoundDocument(
551544
"Tried to deserialize a found document from a missing document.");
552545

553546
DocumentKey key = DecodeKey(reader, response.found.name);
554-
FieldValue::Map value =
547+
ObjectValue value =
555548
DecodeFields(reader, response.found.fields_count, response.found.fields);
556549
SnapshotVersion version = DecodeVersion(reader, response.found.update_time);
557550

558551
if (version == SnapshotVersion::None()) {
559552
reader->Fail("Got a document response with no snapshot version");
560553
}
561554

562-
return Document(ObjectValue::FromMap(std::move(value)), std::move(key),
563-
version, DocumentState::kSynced);
555+
return Document(std::move(value), std::move(key), version,
556+
DocumentState::kSynced);
564557
}
565558

566559
NoDocument Serializer::DecodeMissingDocument(
@@ -582,17 +575,6 @@ NoDocument Serializer::DecodeMissingDocument(
582575
/*has_committed_mutations=*/false);
583576
}
584577

585-
Document Serializer::DecodeDocument(
586-
Reader* reader, const google_firestore_v1_Document& proto) const {
587-
FieldValue::Map fields_internal =
588-
DecodeFields(reader, proto.fields_count, proto.fields);
589-
SnapshotVersion version = DecodeVersion(reader, proto.update_time);
590-
591-
return Document(ObjectValue::FromMap(std::move(fields_internal)),
592-
DecodeKey(reader, proto.name), version,
593-
DocumentState::kSynced);
594-
}
595-
596578
google_firestore_v1_Write Serializer::EncodeMutation(
597579
const Mutation& mutation) const {
598580
HARD_ASSERT(mutation.is_valid(), "Invalid mutation encountered.");
@@ -672,8 +654,8 @@ Mutation Serializer::DecodeMutation(
672654
switch (mutation.which_operation) {
673655
case google_firestore_v1_Write_update_tag: {
674656
DocumentKey key = DecodeKey(reader, mutation.update.name);
675-
ObjectValue value = ObjectValue::FromMap(DecodeFields(
676-
reader, mutation.update.fields_count, mutation.update.fields));
657+
ObjectValue value = DecodeFields(reader, mutation.update.fields_count,
658+
mutation.update.fields);
677659
FieldMask mask = DecodeFieldMask(mutation.update_mask);
678660
if (mask.size() > 0) {
679661
return PatchMutation(std::move(key), std::move(value), std::move(mask),
@@ -1634,8 +1616,8 @@ WatchTargetChangeState Serializer::DecodeTargetChangeState(
16341616
std::unique_ptr<WatchChange> Serializer::DecodeDocumentChange(
16351617
nanopb::Reader* reader,
16361618
const google_firestore_v1_DocumentChange& change) const {
1637-
ObjectValue value = ObjectValue::FromMap(DecodeFields(
1638-
reader, change.document.fields_count, change.document.fields));
1619+
ObjectValue value = DecodeFields(reader, change.document.fields_count,
1620+
change.document.fields);
16391621
DocumentKey key = DecodeKey(reader, change.document.name);
16401622

16411623
HARD_ASSERT(change.document.has_update_time,

Firestore/core/src/firebase/firestore/remote/serializer.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,6 @@ class Serializer {
212212
static pb_bytes_array_t* EncodeFieldPath(const model::FieldPath& field_path);
213213
static model::FieldPath DecodeFieldPath(const pb_bytes_array_t* field_path);
214214

215-
model::Document DecodeDocument(
216-
nanopb::Reader* reader, const google_firestore_v1_Document& proto) const;
217-
218215
static google_protobuf_Timestamp EncodeVersion(
219216
const model::SnapshotVersion& version);
220217

@@ -260,6 +257,11 @@ class Serializer {
260257
nanopb::Reader* reader,
261258
const google_firestore_v1_ListenResponse& listen_response) const;
262259

260+
model::ObjectValue DecodeFields(
261+
nanopb::Reader* reader,
262+
size_t count,
263+
const google_firestore_v1_Document_FieldsEntry* fields) const;
264+
263265
// Public for the sake of tests.
264266
google_firestore_v1_StructuredQuery_Filter EncodeFilters(
265267
const core::FilterList& filters) const;
@@ -296,10 +298,6 @@ class Serializer {
296298
model::FieldValue::Map::value_type DecodeFieldsEntry(
297299
nanopb::Reader* reader,
298300
const google_firestore_v1_Document_FieldsEntry& fields) const;
299-
model::FieldValue::Map DecodeFields(
300-
nanopb::Reader* reader,
301-
size_t count,
302-
const google_firestore_v1_Document_FieldsEntry* fields) const;
303301

304302
model::FieldValue::Map DecodeMapValue(
305303
nanopb::Reader* reader,

0 commit comments

Comments
 (0)