Skip to content

Commit fe7e4a1

Browse files
authored
Simplify Serializer [En|De]code*() methods to no longer [de]serialize manually. (#1816)
Now, we use the re-worked nanopb deserializer (that mallocs). This also had the advantage of moving the C++ serialization code closer to the other platforms. Still TODO: - Reorder the methods within the files to resemble the other platforms.
1 parent c4b4ded commit fe7e4a1

File tree

14 files changed

+722
-1365
lines changed

14 files changed

+722
-1365
lines changed

FirebaseFirestore.podspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling,
5555
s.dependency 'gRPC-C++', '~> 0.0.3'
5656
s.dependency 'leveldb-library', '~> 1.20'
5757
s.dependency 'Protobuf', '~> 3.1'
58-
s.dependency 'nanopb', '~> 0.3.8'
58+
s.dependency 'nanopb', '~> 0.3.901'
5959

6060
s.frameworks = 'MobileCoreServices', 'SystemConfiguration'
6161
s.library = 'c++'
@@ -66,7 +66,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling,
6666
# The nanopb pod sets these defs, so we must too. (We *do* require 16bit
6767
# (or larger) fields, so we'd have to set at least PB_FIELD_16BIT
6868
# anyways.)
69-
'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1',
69+
'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1',
7070
'HEADER_SEARCH_PATHS' =>
7171
'"${PODS_TARGET_SRCROOT}" ' +
7272
'"${PODS_TARGET_SRCROOT}/Firestore/third_party/abseil-cpp" ' +

Firestore/Example/Firestore.xcodeproj/project.pbxproj

+2
Original file line numberDiff line numberDiff line change
@@ -2484,6 +2484,7 @@
24842484
"\"${PODS_ROOT}/GoogleTest/googlemock/include\"",
24852485
"\"${PODS_ROOT}/GoogleTest/googletest/include\"",
24862486
"\"${PODS_ROOT}/leveldb-library/include\"",
2487+
"\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"",
24872488
"\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"",
24882489
"\"${PODS_ROOT}/ProtobufCpp/src\"",
24892490
);
@@ -2569,6 +2570,7 @@
25692570
"\"${PODS_ROOT}/GoogleTest/googlemock/include\"",
25702571
"\"${PODS_ROOT}/GoogleTest/googletest/include\"",
25712572
"\"${PODS_ROOT}/leveldb-library/include\"",
2573+
"\"${PODS_ROOT}/../../../Firestore/Protos/nanopb\"",
25722574
"\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"",
25732575
"\"${PODS_ROOT}/ProtobufCpp/src\"",
25742576
);

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

+112-171
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
#include "Firestore/core/src/firebase/firestore/model/field_value.h"
2828
#include "Firestore/core/src/firebase/firestore/model/no_document.h"
2929
#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
30-
#include "Firestore/core/src/firebase/firestore/nanopb/tag.h"
3130
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
31+
#include "Firestore/core/src/firebase/firestore/util/string_format.h"
3232

3333
namespace firebase {
3434
namespace firestore {
@@ -41,28 +41,27 @@ using model::NoDocument;
4141
using model::ObjectValue;
4242
using model::SnapshotVersion;
4343
using nanopb::Reader;
44-
using nanopb::Tag;
4544
using nanopb::Writer;
45+
using remote::MakeArray;
4646
using util::Status;
47+
using util::StringFormat;
48+
49+
firestore_client_MaybeDocument LocalSerializer::EncodeMaybeDocument(
50+
const MaybeDocument& maybe_doc) const {
51+
firestore_client_MaybeDocument result{};
4752

48-
void LocalSerializer::EncodeMaybeDocument(
49-
Writer* writer, const MaybeDocument& maybe_doc) const {
5053
switch (maybe_doc.type()) {
5154
case MaybeDocument::Type::Document:
52-
writer->WriteTag(
53-
{PB_WT_STRING, firestore_client_MaybeDocument_document_tag});
54-
writer->WriteNestedMessage([&](Writer* writer) {
55-
EncodeDocument(writer, static_cast<const Document&>(maybe_doc));
56-
});
57-
return;
55+
result.which_document_type = firestore_client_MaybeDocument_document_tag;
56+
result.document = EncodeDocument(static_cast<const Document&>(maybe_doc));
57+
return result;
5858

5959
case MaybeDocument::Type::NoDocument:
60-
writer->WriteTag(
61-
{PB_WT_STRING, firestore_client_MaybeDocument_no_document_tag});
62-
writer->WriteNestedMessage([&](Writer* writer) {
63-
EncodeNoDocument(writer, static_cast<const NoDocument&>(maybe_doc));
64-
});
65-
return;
60+
result.which_document_type =
61+
firestore_client_MaybeDocument_no_document_tag;
62+
result.no_document =
63+
EncodeNoDocument(static_cast<const NoDocument&>(maybe_doc));
64+
return result;
6665

6766
case MaybeDocument::Type::Unknown:
6867
// TODO(rsgowman)
@@ -73,195 +72,137 @@ void LocalSerializer::EncodeMaybeDocument(
7372
}
7473

7574
std::unique_ptr<MaybeDocument> LocalSerializer::DecodeMaybeDocument(
76-
Reader* reader) const {
77-
std::unique_ptr<MaybeDocument> result;
78-
79-
while (reader->good()) {
80-
switch (reader->ReadTag()) {
81-
case firestore_client_MaybeDocument_document_tag:
82-
// TODO(rsgowman): If multiple 'document' values are found, we should
83-
// merge them (rather than using the last one.)
84-
result = reader->ReadNestedMessage<Document>(
85-
rpc_serializer_, &remote::Serializer::DecodeDocument);
86-
break;
87-
88-
case firestore_client_MaybeDocument_no_document_tag:
89-
// TODO(rsgowman): If multiple 'no_document' values are found, we should
90-
// merge them (rather than using the last one.)
91-
result = reader->ReadNestedMessage<NoDocument>(
92-
*this, &LocalSerializer::DecodeNoDocument);
93-
break;
94-
95-
default:
96-
reader->SkipUnknown();
97-
}
98-
}
75+
Reader* reader, const firestore_client_MaybeDocument& proto) const {
76+
if (!reader->status().ok()) return nullptr;
9977

100-
if (!result) {
101-
reader->Fail(
102-
"Invalid MaybeDocument message: Neither 'no_document' nor 'document' "
103-
"fields set.");
104-
return nullptr;
78+
switch (proto.which_document_type) {
79+
case firestore_client_MaybeDocument_document_tag:
80+
return rpc_serializer_.DecodeDocument(reader, proto.document);
81+
82+
case firestore_client_MaybeDocument_no_document_tag:
83+
return DecodeNoDocument(reader, proto.no_document);
84+
85+
default:
86+
reader->Fail(
87+
StringFormat("Invalid MaybeDocument document type: %s. Expected "
88+
"'no_document' (%s) or 'document' (%s)",
89+
proto.which_document_type,
90+
firestore_client_MaybeDocument_no_document_tag,
91+
firestore_client_MaybeDocument_document_tag));
92+
return nullptr;
10593
}
106-
return result;
94+
95+
UNREACHABLE();
10796
}
10897

109-
void LocalSerializer::EncodeDocument(Writer* writer,
110-
const Document& doc) const {
111-
// Encode Document.name
112-
writer->WriteTag({PB_WT_STRING, google_firestore_v1beta1_Document_name_tag});
113-
writer->WriteString(rpc_serializer_.EncodeKey(doc.key()));
98+
google_firestore_v1beta1_Document LocalSerializer::EncodeDocument(
99+
const Document& doc) const {
100+
google_firestore_v1beta1_Document result{};
101+
102+
result.name =
103+
rpc_serializer_.EncodeString(rpc_serializer_.EncodeKey(doc.key()));
114104

115105
// Encode Document.fields (unless it's empty)
116-
const ObjectValue& object_value = doc.data().object_value();
117-
if (!object_value.internal_value.empty()) {
118-
rpc_serializer_.EncodeObjectMap(
119-
writer, object_value.internal_value,
120-
google_firestore_v1beta1_Document_fields_tag,
121-
google_firestore_v1beta1_Document_FieldsEntry_key_tag,
122-
google_firestore_v1beta1_Document_FieldsEntry_value_tag);
106+
size_t count = doc.data().object_value().internal_value.size();
107+
result.fields_count = count;
108+
result.fields =
109+
MakeArray<google_firestore_v1beta1_Document_FieldsEntry>(count);
110+
int i = 0;
111+
for (const auto& kv : doc.data().object_value().internal_value) {
112+
result.fields[i].key = rpc_serializer_.EncodeString(kv.first);
113+
result.fields[i].value = rpc_serializer_.EncodeFieldValue(kv.second);
114+
i++;
123115
}
124116

125-
// Encode Document.update_time
126-
writer->WriteTag(
127-
{PB_WT_STRING, google_firestore_v1beta1_Document_update_time_tag});
128-
writer->WriteNestedMessage([&](Writer* writer) {
129-
rpc_serializer_.EncodeVersion(writer, doc.version());
130-
});
117+
result.update_time = rpc_serializer_.EncodeVersion(doc.version());
131118

132119
// Ignore Document.create_time. (We don't use this in our on-disk protos.)
120+
121+
return result;
133122
}
134123

135-
void LocalSerializer::EncodeNoDocument(Writer* writer,
136-
const NoDocument& no_doc) const {
137-
// Encode NoDocument.name
138-
writer->WriteTag({PB_WT_STRING, firestore_client_NoDocument_name_tag});
139-
writer->WriteString(rpc_serializer_.EncodeKey(no_doc.key()));
140-
141-
// Encode NoDocument.read_time
142-
writer->WriteTag({PB_WT_STRING, firestore_client_NoDocument_read_time_tag});
143-
writer->WriteNestedMessage([&](Writer* writer) {
144-
rpc_serializer_.EncodeVersion(writer, no_doc.version());
145-
});
124+
firestore_client_NoDocument LocalSerializer::EncodeNoDocument(
125+
const NoDocument& no_doc) const {
126+
firestore_client_NoDocument result{};
127+
128+
result.name =
129+
rpc_serializer_.EncodeString(rpc_serializer_.EncodeKey(no_doc.key()));
130+
result.read_time = rpc_serializer_.EncodeVersion(no_doc.version());
131+
132+
return result;
146133
}
147134

148135
std::unique_ptr<NoDocument> LocalSerializer::DecodeNoDocument(
149-
Reader* reader) const {
150-
std::string name;
151-
absl::optional<SnapshotVersion> version = SnapshotVersion::None();
152-
153-
while (reader->good()) {
154-
switch (reader->ReadTag()) {
155-
case firestore_client_NoDocument_name_tag:
156-
name = reader->ReadString();
157-
break;
158-
159-
case firestore_client_NoDocument_read_time_tag:
160-
version = reader->ReadNestedMessage<SnapshotVersion>(
161-
rpc_serializer_.DecodeSnapshotVersion);
162-
break;
163-
164-
default:
165-
reader->SkipUnknown();
166-
break;
167-
}
168-
}
169-
136+
Reader* reader, const firestore_client_NoDocument& proto) const {
170137
if (!reader->status().ok()) return nullptr;
171-
return absl::make_unique<NoDocument>(rpc_serializer_.DecodeKey(name),
172-
*std::move(version));
173-
}
174138

175-
void LocalSerializer::EncodeQueryData(Writer* writer,
176-
const QueryData& query_data) const {
177-
writer->WriteTag({PB_WT_VARINT, firestore_client_Target_target_id_tag});
178-
writer->WriteInteger(query_data.target_id());
139+
SnapshotVersion version =
140+
rpc_serializer_.DecodeSnapshotVersion(reader, proto.read_time);
179141

180-
writer->WriteTag(
181-
{PB_WT_VARINT, firestore_client_Target_last_listen_sequence_number_tag});
182-
writer->WriteInteger(query_data.sequence_number());
142+
if (!reader->status().ok()) return nullptr;
143+
return absl::make_unique<NoDocument>(
144+
rpc_serializer_.DecodeKey(reader,
145+
rpc_serializer_.DecodeString(proto.name)),
146+
std::move(version));
147+
}
183148

184-
writer->WriteTag(
185-
{PB_WT_STRING, firestore_client_Target_snapshot_version_tag});
186-
writer->WriteNestedMessage([&](Writer* writer) {
187-
rpc_serializer_.EncodeTimestamp(writer,
188-
query_data.snapshot_version().timestamp());
189-
});
149+
firestore_client_Target LocalSerializer::EncodeQueryData(
150+
const QueryData& query_data) const {
151+
firestore_client_Target result{};
190152

191-
writer->WriteTag({PB_WT_STRING, firestore_client_Target_resume_token_tag});
192-
writer->WriteBytes(query_data.resume_token());
153+
result.target_id = query_data.target_id();
154+
result.last_listen_sequence_number = query_data.sequence_number();
155+
result.snapshot_version = rpc_serializer_.EncodeTimestamp(
156+
query_data.snapshot_version().timestamp());
157+
result.resume_token = rpc_serializer_.EncodeBytes(query_data.resume_token());
193158

194159
const Query& query = query_data.query();
195160
if (query.IsDocumentQuery()) {
196161
// TODO(rsgowman): Implement. Probably like this (once EncodeDocumentsTarget
197162
// exists):
198163
/*
199-
writer->WriteTag({PB_WT_STRING, firestore_client_Target_documents_tag});
200-
writer->WriteNestedMessage([&](Writer* writer) {
201-
rpc_serializer_.EncodeDocumentsTarget(writer, query);
202-
});
164+
result.which_target_type = firestore_client_Target_document_tag;
165+
result.documents = rpc_serializer_.EncodeDocumentsTarget(query);
203166
*/
204167
abort();
205168
} else {
206-
writer->WriteTag({PB_WT_STRING, firestore_client_Target_query_tag});
207-
writer->WriteNestedMessage([&](Writer* writer) {
208-
rpc_serializer_.EncodeQueryTarget(writer, query);
209-
});
169+
result.which_target_type = firestore_client_Target_query_tag;
170+
result.query = rpc_serializer_.EncodeQueryTarget(query);
210171
}
172+
173+
return result;
211174
}
212175

213-
absl::optional<QueryData> LocalSerializer::DecodeQueryData(
214-
Reader* reader) const {
215-
model::TargetId target_id = 0;
216-
model::ListenSequenceNumber sequence_number = 0;
217-
absl::optional<SnapshotVersion> version = SnapshotVersion::None();
218-
std::vector<uint8_t> resume_token;
219-
absl::optional<Query> query = Query::Invalid();
220-
221-
while (reader->good()) {
222-
switch (reader->ReadTag()) {
223-
case firestore_client_Target_target_id_tag:
224-
// TODO(rsgowman): How to handle truncation of integer types?
225-
target_id = static_cast<model::TargetId>(reader->ReadInteger());
226-
break;
227-
228-
case firestore_client_Target_last_listen_sequence_number_tag:
229-
// TODO(rsgowman): How to handle truncation of integer types?
230-
sequence_number =
231-
static_cast<model::ListenSequenceNumber>(reader->ReadInteger());
232-
break;
233-
234-
case firestore_client_Target_snapshot_version_tag:
235-
version = reader->ReadNestedMessage<SnapshotVersion>(
236-
rpc_serializer_.DecodeSnapshotVersion);
237-
break;
238-
239-
case firestore_client_Target_resume_token_tag:
240-
resume_token = reader->ReadBytes();
241-
break;
242-
243-
case firestore_client_Target_query_tag:
244-
// TODO(rsgowman): Clear 'documents' field (since query and documents
245-
// are part of a 'oneof').
246-
query =
247-
reader->ReadNestedMessage<Query>(rpc_serializer_.DecodeQueryTarget);
248-
break;
249-
250-
case firestore_client_Target_documents_tag:
251-
// Clear 'query' field (since query and documents are part of a 'oneof')
252-
query = Query::Invalid();
253-
// TODO(rsgowman): Implement.
254-
abort();
255-
256-
default:
257-
reader->SkipUnknown();
258-
break;
259-
}
176+
QueryData LocalSerializer::DecodeQueryData(
177+
Reader* reader, const firestore_client_Target& proto) const {
178+
if (!reader->status().ok()) return QueryData::Invalid();
179+
180+
model::TargetId target_id = proto.target_id;
181+
// TODO(rgowman): How to handle truncation of integer types?
182+
model::ListenSequenceNumber sequence_number = static_cast<model::ListenSequenceNumber>(proto.last_listen_sequence_number);
183+
SnapshotVersion version =
184+
rpc_serializer_.DecodeSnapshotVersion(reader, proto.snapshot_version);
185+
std::vector<uint8_t> resume_token =
186+
rpc_serializer_.DecodeBytes(proto.resume_token);
187+
Query query = Query::Invalid();
188+
189+
switch (proto.which_target_type) {
190+
case firestore_client_Target_query_tag:
191+
query = rpc_serializer_.DecodeQueryTarget(reader, proto.query);
192+
break;
193+
194+
case firestore_client_Target_documents_tag:
195+
// TODO(rsgowman): Implement.
196+
abort();
197+
198+
default:
199+
reader->Fail(
200+
StringFormat("Unknown target_type: %s", proto.which_target_type));
260201
}
261202

262-
if (!reader->status().ok()) return absl::nullopt;
263-
return QueryData(*std::move(query), target_id, sequence_number,
264-
QueryPurpose::kListen, *std::move(version),
203+
if (!reader->status().ok()) return QueryData::Invalid();
204+
return QueryData(std::move(query), target_id, sequence_number,
205+
QueryPurpose::kListen, std::move(version),
265206
std::move(resume_token));
266207
}
267208

0 commit comments

Comments
 (0)