Skip to content

Commit d461324

Browse files
committed
Simplify Serializer Decode*() methods to no longer deserialize manually.
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. - Rework error handling. - Encode*() methods.
1 parent 0da269c commit d461324

File tree

8 files changed

+379
-480
lines changed

8 files changed

+379
-480
lines changed

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

Lines changed: 46 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
3030
#include "Firestore/core/src/firebase/firestore/nanopb/tag.h"
3131
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
32+
#include "Firestore/core/src/firebase/firestore/util/string_format.h"
3233

3334
namespace firebase {
3435
namespace firestore {
@@ -44,6 +45,7 @@ using nanopb::Reader;
4445
using nanopb::Tag;
4546
using nanopb::Writer;
4647
using util::Status;
48+
using util::StringFormat;
4749

4850
void LocalSerializer::EncodeMaybeDocument(
4951
Writer* writer, const MaybeDocument& maybe_doc) const {
@@ -73,37 +75,24 @@ void LocalSerializer::EncodeMaybeDocument(
7375
}
7476

7577
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-
}
78+
Reader* reader, const firestore_client_MaybeDocument& proto) const {
79+
if (!reader->status().ok()) return nullptr;
80+
81+
switch (proto.which_document_type) {
82+
case firestore_client_MaybeDocument_document_tag:
83+
return rpc_serializer_.DecodeDocument(reader, proto.document);
84+
85+
case firestore_client_MaybeDocument_no_document_tag:
86+
return DecodeNoDocument(reader, proto.no_document);
9987

100-
if (!result) {
101-
reader->Fail(
102-
"Invalid MaybeDocument message: Neither 'no_document' nor 'document' "
103-
"fields set.");
104-
return nullptr;
88+
default:
89+
reader->Fail(
90+
"Invalid MaybeDocument message: Neither 'no_document' nor 'document' "
91+
"fields set.");
92+
return nullptr;
10593
}
106-
return result;
94+
95+
UNREACHABLE();
10796
}
10897

10998
void LocalSerializer::EncodeDocument(Writer* writer,
@@ -146,30 +135,16 @@ void LocalSerializer::EncodeNoDocument(Writer* writer,
146135
}
147136

148137
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-
}
138+
Reader* reader, const firestore_client_NoDocument& proto) const {
139+
if (!reader->status().ok()) return nullptr;
140+
141+
absl::optional<SnapshotVersion> version =
142+
rpc_serializer_.DecodeSnapshotVersion(reader, proto.read_time);
169143

170144
if (!reader->status().ok()) return nullptr;
171-
return absl::make_unique<NoDocument>(rpc_serializer_.DecodeKey(name),
172-
*std::move(version));
145+
return absl::make_unique<NoDocument>(
146+
rpc_serializer_.DecodeKey(rpc_serializer_.DecodeString(proto.name)),
147+
*std::move(version));
173148
}
174149

175150
void LocalSerializer::EncodeQueryData(Writer* writer,
@@ -207,45 +182,28 @@ void LocalSerializer::EncodeQueryData(Writer* writer,
207182
}
208183

209184
absl::optional<QueryData> LocalSerializer::DecodeQueryData(
210-
Reader* reader) const {
211-
model::TargetId target_id = 0;
212-
absl::optional<SnapshotVersion> version = SnapshotVersion::None();
213-
std::vector<uint8_t> resume_token;
185+
Reader* reader, const firestore_client_Target& proto) const {
186+
if (!reader->status().ok()) return absl::nullopt;
187+
188+
model::TargetId target_id = proto.target_id;
189+
absl::optional<SnapshotVersion> version =
190+
rpc_serializer_.DecodeSnapshotVersion(reader, proto.snapshot_version);
191+
std::vector<uint8_t> resume_token =
192+
rpc_serializer_.DecodeBytes(proto.resume_token);
214193
absl::optional<Query> query = Query::Invalid();
215194

216-
while (reader->good()) {
217-
switch (reader->ReadTag()) {
218-
case firestore_client_Target_target_id_tag:
219-
// TODO(rsgowman): How to handle truncation of integer types?
220-
target_id = static_cast<model::TargetId>(reader->ReadInteger());
221-
break;
222-
223-
case firestore_client_Target_snapshot_version_tag:
224-
version = reader->ReadNestedMessage<SnapshotVersion>(
225-
rpc_serializer_.DecodeSnapshotVersion);
226-
break;
227-
228-
case firestore_client_Target_resume_token_tag:
229-
resume_token = reader->ReadBytes();
230-
break;
231-
232-
case firestore_client_Target_query_tag:
233-
// TODO(rsgowman): Clear 'documents' field (since query and documents
234-
// are part of a 'oneof').
235-
query =
236-
reader->ReadNestedMessage<Query>(rpc_serializer_.DecodeQueryTarget);
237-
break;
238-
239-
case firestore_client_Target_documents_tag:
240-
// Clear 'query' field (since query and documents are part of a 'oneof')
241-
query = Query::Invalid();
242-
// TODO(rsgowman): Implement.
243-
abort();
244-
245-
default:
246-
reader->SkipUnknown();
247-
break;
248-
}
195+
switch (proto.which_target_type) {
196+
case firestore_client_Target_query_tag:
197+
query = rpc_serializer_.DecodeQueryTarget(reader, proto.query);
198+
break;
199+
200+
case firestore_client_Target_documents_tag:
201+
// TODO(rsgowman): Implement.
202+
abort();
203+
204+
default:
205+
reader->Fail(
206+
StringFormat("Unknown target_type: %s", proto.which_target_type));
249207
}
250208

251209
if (!reader->status().ok()) return absl::nullopt;

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <memory>
2121
#include <vector>
2222

23+
#include "Firestore/Protos/nanopb/firestore/local/maybe_document.nanopb.h"
24+
#include "Firestore/Protos/nanopb/firestore/local/target.nanopb.h"
2325
#include "Firestore/core/src/firebase/firestore/local/query_data.h"
2426
#include "Firestore/core/src/firebase/firestore/model/document.h"
2527
#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
@@ -62,19 +64,19 @@ class LocalSerializer {
6264
const model::MaybeDocument& maybe_doc) const;
6365

6466
/**
65-
* @brief Decodes bytes representing a MaybeDocument proto to the equivalent
66-
* model.
67+
* @brief Decodes nanopb proto representing a MaybeDocument proto to the
68+
* equivalent model.
6769
*
6870
* Check reader->status() to determine if an error occured while decoding.
6971
*
70-
* @param reader The reader object containing the bytes to convert. It's
71-
* assumed that exactly all of the bytes will be used by this conversion.
72+
* @param reader The Reader object. Used only for error handling.
7273
* @return The model equivalent of the bytes or nullopt if an error occurred.
7374
* @post (reader->status().ok() && result) ||
7475
* (!reader->status().ok() && !result)
7576
*/
7677
std::unique_ptr<model::MaybeDocument> DecodeMaybeDocument(
77-
nanopb::Reader* reader) const;
78+
nanopb::Reader* reader,
79+
const firestore_client_MaybeDocument& proto) const;
7880

7981
/**
8082
* @brief Encodes a QueryData to the equivalent bytes, representing a
@@ -88,19 +90,19 @@ class LocalSerializer {
8890
const QueryData& query_data) const;
8991

9092
/**
91-
* @brief Decodes bytes representing a ::firestore::proto::Target proto to the
92-
* equivalent QueryData.
93+
* @brief Decodes nanopb proto representing a ::firestore::proto::Target proto
94+
* to the equivalent QueryData.
9395
*
94-
* Check writer->status() to determine if an error occured while decoding.
96+
* Check reader->status() to determine if an error occured while decoding.
9597
*
96-
* @param reader The reader object containing the bytes to convert. It's
97-
* assumed that exactly all of the bytes will be used by this conversion.
98+
* @param reader The Reader object. Used only for error handling.
9899
* @return The QueryData equivalent of the bytes or nullopt if an error
99100
* occurred.
100101
* @post (reader->status().ok() && result.has_value()) ||
101102
* (!reader->status().ok() && !result.has_value())
102103
*/
103-
absl::optional<QueryData> DecodeQueryData(nanopb::Reader* reader) const;
104+
absl::optional<QueryData> DecodeQueryData(
105+
nanopb::Reader* reader, const firestore_client_Target& proto) const;
104106

105107
private:
106108
/**
@@ -114,7 +116,7 @@ class LocalSerializer {
114116
const model::NoDocument& no_doc) const;
115117

116118
std::unique_ptr<model::NoDocument> DecodeNoDocument(
117-
nanopb::Reader* reader) const;
119+
nanopb::Reader* reader, const firestore_client_NoDocument& proto) const;
118120

119121
const remote::Serializer& rpc_serializer_;
120122
};

Firestore/core/src/firebase/firestore/nanopb/reader.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ void Reader::ReadNanopbMessage(const pb_field_t fields[], void* dest_struct) {
7373
}
7474
}
7575

76+
void Reader::FreeNanopbMessage(const pb_field_t fields[], void* dest_struct) {
77+
pb_release(fields, dest_struct);
78+
}
79+
7680
/**
7781
* Note that (despite the return type) this works for bool, enum, int32, int64,
7882
* uint32 and uint64 proto field types.

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,27 @@ class Reader {
8181
/**
8282
* Reads a nanopb message from the input stream.
8383
*
84-
* This essentially wraps calls to nanopb's pb_decode() method. If we didn't
85-
* use `oneof`s in our protos, this would be the primary way of decoding
86-
* messages.
84+
* This essentially wraps calls to nanopb's pb_decode() method. This is the
85+
* primary way of decoding messages.
86+
*
87+
* Note that this allocates memory. You must call FreeNanopbMessage() (which
88+
* essentially wraps pb_release()) on the dest_struct in order to avoid memory
89+
* leaks. (This also implies code that uses this is not exception safe.)
8790
*/
91+
// TODO(rsgowman): At the moment we rely on the caller to manually free
92+
// dest_struct via FreeNanopbMessage(). We might instead see if we can
93+
// register allocated messages, track them, and free them ourselves. This may
94+
// be especially relevant if we start to use nanopb messages as the underlying
95+
// data within the model objects.
8896
void ReadNanopbMessage(const pb_field_t fields[], void* dest_struct);
8997

98+
/**
99+
* Release memory allocated by ReadNanopbMessage().
100+
*
101+
* This essentially wraps calls to nanopb's pb_release() method.
102+
*/
103+
void FreeNanopbMessage(const pb_field_t fields[], void* dest_struct);
104+
90105
void ReadNull();
91106
bool ReadBool();
92107
std::int64_t ReadInteger();

0 commit comments

Comments
 (0)