Skip to content

Commit 35de3c5

Browse files
rsgowmanwilhuff
authored andcommitted
Move creation of pb_ostream_t's into Writer (#920)
* Move creation of pb_ostream_t's into Writer Writer now owns these structs * Writer::FromBuffer -> Writer::Wrap * add todo for possible future simplification.
1 parent 15bec81 commit 35de3c5

File tree

2 files changed

+73
-46
lines changed

2 files changed

+73
-46
lines changed

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

Lines changed: 70 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,28 @@ std::map<std::string, FieldValue> DecodeObject(pb_istream_t* stream);
4343

4444
/**
4545
* Docs TODO(rsgowman). But currently, this just wraps the underlying nanopb
46-
* pb_ostream_t. Eventually, this might use static factory methods to create the
47-
* underlying pb_ostream_t rather than directly passing it in.
46+
* pb_ostream_t.
4847
*/
4948
// TODO(rsgowman): Encode* -> Write*
5049
class Writer {
5150
public:
52-
explicit Writer(pb_ostream_t* stream) : stream_(stream) {
51+
/**
52+
* Creates an output stream that writes to the specified vector. Note that
53+
* this vector pointer must remain valid for the lifetime of this Writer.
54+
*
55+
* (This is roughly equivalent to the nanopb function
56+
* pb_ostream_from_buffer())
57+
*
58+
* @param out_bytes where the output should be serialized to.
59+
*/
60+
static Writer Wrap(std::vector<uint8_t>* out_bytes);
61+
62+
/**
63+
* Creates a non-writing output stream used to calculate the size of
64+
* the serialized output.
65+
*/
66+
static Writer SizingStream() {
67+
return Writer(PB_OSTREAM_SIZING);
5368
}
5469

5570
/**
@@ -89,10 +104,18 @@ class Writer {
89104
const std::function<void(Writer*)>& encode_message_fn);
90105

91106
size_t bytes_written() const {
92-
return stream_->bytes_written;
107+
return stream_.bytes_written;
93108
}
94109

95110
private:
111+
/**
112+
* Creates a new Writer, based on the given nanopb pb_ostream_t. Note that
113+
* a shallow copy will be taken. (Non-null pointers within this struct must
114+
* remain valid for the lifetime of this Writer.)
115+
*/
116+
explicit Writer(const pb_ostream_t& stream) : stream_(stream) {
117+
}
118+
96119
/**
97120
* Encodes a "varint" to the output stream.
98121
*
@@ -108,17 +131,44 @@ class Writer {
108131
*/
109132
void EncodeVarint(uint64_t value);
110133

111-
pb_ostream_t* stream_;
134+
pb_ostream_t stream_;
112135
};
113136

137+
Writer Writer::Wrap(std::vector<uint8_t>* out_bytes) {
138+
// TODO(rsgowman): find a better home for this constant.
139+
// A document is defined to have a max size of 1MiB - 4 bytes.
140+
static const size_t kMaxDocumentSize = 1 * 1024 * 1024 - 4;
141+
142+
// Construct a nanopb output stream.
143+
//
144+
// Set the max_size to be the max document size (as an upper bound; one would
145+
// expect individual FieldValue's to be smaller than this).
146+
//
147+
// bytes_written is (always) initialized to 0. (NB: nanopb does not know or
148+
// care about the underlying output vector, so where we are in the vector
149+
// itself is irrelevant. i.e. don't use out_bytes->size())
150+
pb_ostream_t raw_stream = {
151+
/*callback=*/[](pb_ostream_t* stream, const pb_byte_t* buf,
152+
size_t count) -> bool {
153+
auto* out_bytes = static_cast<std::vector<uint8_t>*>(stream->state);
154+
out_bytes->insert(out_bytes->end(), buf, buf + count);
155+
return true;
156+
},
157+
/*state=*/out_bytes,
158+
/*max_size=*/kMaxDocumentSize,
159+
/*bytes_written=*/0,
160+
/*errmsg=*/nullptr};
161+
return Writer(raw_stream);
162+
}
163+
114164
// TODO(rsgowman): I've left the methods as near as possible to where they were
115165
// before, which implies that the Writer methods are interspersed with the
116166
// PbIstream methods (or what will become the PbIstream methods). This should
117167
// make it a bit easier to review. Refactor these to group the related methods
118168
// together (probably within their own file rather than here).
119169

120170
void Writer::EncodeTag(pb_wire_type_t wiretype, uint32_t field_number) {
121-
bool status = pb_encode_tag(stream_, wiretype, field_number);
171+
bool status = pb_encode_tag(&stream_, wiretype, field_number);
122172
if (!status) {
123173
// TODO(rsgowman): figure out error handling
124174
abort();
@@ -130,7 +180,7 @@ void Writer::EncodeSize(size_t size) {
130180
}
131181

132182
void Writer::EncodeVarint(uint64_t value) {
133-
bool status = pb_encode_varint(stream_, value);
183+
bool status = pb_encode_varint(&stream_, value);
134184
if (!status) {
135185
// TODO(rsgowman): figure out error handling
136186
abort();
@@ -195,7 +245,7 @@ int64_t DecodeInteger(pb_istream_t* stream) {
195245

196246
void Writer::EncodeString(const std::string& string_value) {
197247
bool status = pb_encode_string(
198-
stream_, reinterpret_cast<const pb_byte_t*>(string_value.c_str()),
248+
&stream_, reinterpret_cast<const pb_byte_t*>(string_value.c_str()),
199249
string_value.length());
200250
if (!status) {
201251
// TODO(rsgowman): figure out error handling
@@ -332,8 +382,7 @@ FieldValue DecodeFieldValueImpl(pb_istream_t* stream) {
332382
void Writer::EncodeNestedMessage(
333383
const std::function<void(Writer*)>& encode_message_fn) {
334384
// First calculate the message size using a non-writing substream.
335-
pb_ostream_t raw_sizing_substream = PB_OSTREAM_SIZING;
336-
Writer sizing_substream(&raw_sizing_substream);
385+
Writer sizing_substream = Writer::SizingStream();
337386
encode_message_fn(&sizing_substream);
338387
size_t size = sizing_substream.bytes_written();
339388

@@ -344,8 +393,8 @@ void Writer::EncodeNestedMessage(
344393
// parse field_value a second time; just update the bytes_written via a call
345394
// to pb_write. (If we try to write the contents into a sizing stream, it'll
346395
// fail since sizing streams don't actually have any buffer space.)
347-
if (stream_->callback == nullptr) {
348-
bool status = pb_write(stream_, nullptr, size);
396+
if (stream_.callback == nullptr) {
397+
bool status = pb_write(&stream_, nullptr, size);
349398
if (!status) {
350399
// TODO(rsgowman): figure out error handling
351400
abort();
@@ -354,7 +403,7 @@ void Writer::EncodeNestedMessage(
354403
}
355404

356405
// Ensure the output stream has enough space
357-
if (stream_->bytes_written + size > stream_->max_size) {
406+
if (stream_.bytes_written + size > stream_.max_size) {
358407
// TODO(rsgowman): figure out error handling
359408
abort();
360409
}
@@ -363,18 +412,16 @@ void Writer::EncodeNestedMessage(
363412
// did the first time. (Use an initializer rather than setting fields
364413
// individually like nanopb does. This gives us a *chance* of noticing if
365414
// nanopb adds new fields.)
366-
pb_ostream_t raw_writing_substream = {stream_->callback, stream_->state,
367-
/*max_size=*/size,
368-
/*bytes_written=*/0,
369-
/*errmsg=*/nullptr};
370-
Writer writing_substream(&raw_writing_substream);
415+
Writer writing_substream({stream_.callback, stream_.state,
416+
/*max_size=*/size, /*bytes_written=*/0,
417+
/*errmsg=*/nullptr});
371418
encode_message_fn(&writing_substream);
372419

373-
stream_->bytes_written += raw_writing_substream.bytes_written;
374-
stream_->state = raw_writing_substream.state;
375-
stream_->errmsg = raw_writing_substream.errmsg;
420+
stream_.bytes_written += writing_substream.stream_.bytes_written;
421+
stream_.state = writing_substream.stream_.state;
422+
stream_.errmsg = writing_substream.stream_.errmsg;
376423

377-
if (raw_writing_substream.bytes_written != size) {
424+
if (writing_substream.bytes_written() != size) {
378425
// submsg size changed
379426
// TODO(rsgowman): figure out error handling
380427
abort();
@@ -520,30 +567,7 @@ std::map<std::string, FieldValue> DecodeObject(pb_istream_t* stream) {
520567

521568
void Serializer::EncodeFieldValue(const FieldValue& field_value,
522569
std::vector<uint8_t>* out_bytes) {
523-
// TODO(rsgowman): find a better home for this constant.
524-
// A document is defined to have a max size of 1MiB - 4 bytes.
525-
static const size_t kMaxDocumentSize = 1 * 1024 * 1024 - 4;
526-
527-
// Construct a nanopb output stream.
528-
//
529-
// Set the max_size to be the max document size (as an upper bound; one would
530-
// expect individual FieldValue's to be smaller than this).
531-
//
532-
// bytes_written is (always) initialized to 0. (NB: nanopb does not know or
533-
// care about the underlying output vector, so where we are in the vector
534-
// itself is irrelevant. i.e. don't use out_bytes->size())
535-
pb_ostream_t raw_stream = {
536-
/*callback=*/[](pb_ostream_t* stream, const pb_byte_t* buf,
537-
size_t count) -> bool {
538-
auto* out_bytes = static_cast<std::vector<uint8_t>*>(stream->state);
539-
out_bytes->insert(out_bytes->end(), buf, buf + count);
540-
return true;
541-
},
542-
/*state=*/out_bytes,
543-
/*max_size=*/kMaxDocumentSize,
544-
/*bytes_written=*/0,
545-
/*errmsg=*/NULL};
546-
Writer stream(&raw_stream);
570+
Writer stream = Writer::Wrap(out_bytes);
547571
EncodeFieldValueImpl(&stream, field_value);
548572
}
549573

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ class Serializer {
6767
* appended to this vector.
6868
*/
6969
// TODO(rsgowman): error handling, incl return code.
70+
// TODO(rsgowman): If we never support any output except to a vector, it may
71+
// make sense to have Serializer own the vector and provide an accessor rather
72+
// than asking the user to create it first.
7073
static void EncodeFieldValue(
7174
const firebase::firestore::model::FieldValue& field_value,
7275
std::vector<uint8_t>* out_bytes);

0 commit comments

Comments
 (0)