Skip to content

[De]Serialize FieldValue map_values ("Objects") #878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 8, 2018

Conversation

rsgowman
Copy link
Member

@rsgowman rsgowman commented Mar 6, 2018

These can (recursively) contain other FieldValues.

rsgowman added 3 commits March 6, 2018 10:32
No code changes; refactoring only. This commit is present only so that I
don't move and change code in the same commit. (i.e. see next commit for
context.)
These can (recursively) contain other FieldValues.
@rsgowman rsgowman requested review from var-const and zxu123 March 6, 2018 15:59
Copy link
Contributor

@zxu123 zxu123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

return fv;
}

void EncodeFieldEntry(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have difficulty to understand what this function is for without looking into implementation. Could you either add docs or change naming?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both please.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name follows the same pattern as the rest: EncodeType, so I'd like to keep that. I've added some docs PTAL and let me know if that clears it up.

Also, there's a minor spelling mistake in the name: FieldEntry -> FieldsEntry.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For naming: I find it a little non-intuitive that an Object created from a map consists of FieldEntries (I understand this is Nanopb terminology). Perhaps MapEntry or ObjectMember?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this method is (currently) specific to FieldsEntry's. It won't work for generic entries in a map. (Note the references to google_firestore_v1beta1_MapValue_FieldsEntry_key_tag and google_firestore_v1beta1_MapValue_FieldsEntry_value_tag within the implementation.)

Would EncodeMapValueFieldsEntry() be clearer?

Copy link
Contributor

@var-const var-const left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review.

@@ -111,6 +111,11 @@ class FieldValue {
return string_value_;
}

const std::map<const std::string, const FieldValue> object_value() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Was the intention to return a const reference? Otherwise, just skip const and return a non-const value. It doesn't prevent the caller from storing this return value in a non-const variable, and it prevents move semantics.
  2. Map's value_type is already defined as having const keys (std::pair<const K, V>), so I suggest you drop const from std::string template parameter (here and in other declarations).
  3. I see that this declaration is reused in serializer. Perhaps introduce an alias? using ObjectT = std::map<...>;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Was the intention to return a const reference? ...

Whoops; yeah was supposed to be a reference. (Fixed.)

2 ...drop const from std::string template parameter...

I agree, but that's a separate change which I'll leave for another CL. ... Hm. No, I changed my mind. That's trivial enough that I'll just do it now. (Fixed.)

3 ...Perhaps introduce an alias? using ObjectT = std::map<...>;

I have no strong opinion on this. Generally, I prefer to avoid convenience aliases as I like to see what the actual type is. But I admit that this would be useful to help trim down the boilerplate. Other votes?


// Named '..Impl' so as to not conflict with Serializer::EncodeFieldValue.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I don't think these names can conflict as far as overloading resolution is concerned. I presume your aim is to distinguish them clearer for the reader? If that's the case, consider rephrasing to something like Named '...Impl' to clearly distinguish from Serializer::EncodeFieldValue (strawman).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, they do conflict. I renamed this to allow the compiler to distinguish them rather than the reader. I originally assumed that they would not, but the compiler doesn't seem to look further once it sees the method present in the class. :(

Update: We decided to keep this for now, but to refactor into a wrapper class, so this will shortly disappear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, I missed your comment with the explanation. So one shadows the other. It's also possible to use the free function as ::EncodeFieldValue, but I guess this code will change quite a bit after refactoring anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't work either. (It was actually the first thing I though to try.) I presume because it's not actually in the global namespace. And yeah, it should change significantly after refactoring.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the declaration in the class shadows the declaration in the containing namespace, so the free function doesn't even participate in the overload resolution. I'm pretty sure that ::firebase::firestore::whatever::NameOfFunction should work (sorry I communicated it poorly), but if it doesn't, guess I'm wrong.

void EncodeObject(
pb_ostream_t* stream,
const std::map<const std::string, const FieldValue>& object_value) {
google_firestore_v1beta1_MapValue mapValue =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this variable name be snake_case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup; fixed.

// investigation to see if we can get nanopb to tell us how much space it's
// going to need. (Hint: use a sizing stream, i.e. PB_OSTREAM_SIZING)
uint8_t buf[1024];
memset(buf, 0x42, sizeof(buf));
Copy link
Contributor

@var-const var-const Mar 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. What's the meaning of 0x42?
  2. Entirely optional: consider std::array. It will be somewhat more verbose, but slightly higher-level:
std::array<uint8_t, 1024> buf;
buf.fill(0x42);
pb_ostream_t stream = pb_ostream_from_buffer(buf.data(), buf.size());

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. This was debugging stuff that I forgot to remove. I don't actually care what it's initialized to. (Removed.)


TEST_F(SerializerTest, EncodesNestedObjectsToBytes) {
// As above, verify max int64_t value.
EXPECT_EQ(9223372036854775807, std::numeric_limits<int64_t>::max());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This comment should explain the purpose of this check. Also, why not use 922... number directly? In case you deliberately test for max() here, it would also be great to explain that in the comment.
  2. This can probably be a static_assert. As far as I can tell, VS 2015 should have numeric_limits<T>::max() as constexpr.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to duplicate the comment from above. Regardless, this (and all other raw mins/maxes) should disappear as soon as we link libprotobuf to the test suite. (Added TODO, but otherwise haven't taken an action as this code will be deleted soon.)

{{"b", FieldValue::TrueValue()},
// TODO(rsgowman): add doubles (once they're supported)
// {"d", FieldValue::DoubleValue(std::numeric_limits<double>::max())},
{"i", FieldValue::IntegerValue(1)},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand it might be preferable to be explicit here, but in general, does our FieldValue interface require calling the static "constructors"? I.e., is it not possible to have:

{"i", 1}, {"n", FieldValue::NullValue()}, {"s", "foo"}, ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently yes. But I'd certainly not object to allowing FieldValues to be constructed like that. (I'll bring it up for discussion in the chat.)

rsgowman added 2 commits March 6, 2018 13:27
- spelling: [En|De]codeFieldEntry -> [En|De]codeFieldsEntry
- docstring added to EncodeFieldsEntry
std::map already declares the key as const, so this is redundant.
@@ -111,6 +111,11 @@ class FieldValue {
return string_value_;
}

const std::map<const std::string, const FieldValue> object_value() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::map already treats the key as const and FieldValue is already immutable. I love const as much as the next guy but this seems like a pretty bonkers type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just after I had removed it from the key. :)

Now removed from value too.

void EncodeObject(
pb_ostream_t* stream,
const std::map<const std::string, const FieldValue>& object_value);
std::map<const std::string, const FieldValue> DecodeObject(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If forward declarations really are required, please put them up at the top of the file so we don't have to hunt for them.

Also, laid out together like this it took me a moment to realize this wasn't a single declaration. Maybe newline in between?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both done.


// Named '..Impl' so as to not conflict with Serializer::EncodeFieldValue.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a useful comment. It focuses on why the names are different without drawing a distinction between the differences in behavior which is the actually interesting part.

Consider two audiences:

  • The first-time reader wading in here for the first time: help them tame the complexity in this file. Help understand the differences between these two. Why would I choose one or the other?
  • The over-zealous new project member that wants to refactor everything: why are these separate? Do they need to be? What breaks if you use the wrong one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, see my previous (long buried) comment on this PR. Or just ignore it. We decided we can probably convert this into a class that wraps the stream struct which will likely cause a number of good things to happen (incl the elimination of this.)

Added a TODO for now.

EncodeString(&sizing_stream, kv.first);
EncodeSubFieldValue(&sizing_stream, kv.second);

// additional 2 for the two tags
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're already using a sizing stream let's just do everything via the sizing stream. The less we have to know the specifics about the bytes layout in here the easier this code will be to work with over the long haul.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I understand this comment.

Or are you suggesting moving the sizing calculation out of this method, and up to the calling location? That would make sense, and is similar to the split between EncodeFieldValue and EncodeNestedFieldValue (although in this case, the FieldsEntry object is only every a nested message.) If so; done. I suspect that'll need to get reworked a bit more during the refactoring.

return fv;
}

void EncodeFieldEntry(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both please.


default:
// TODO(rsgowman): figure out error handling
abort();
}
}

void EncodeSubFieldValue(pb_ostream_t* stream, const FieldValue& field_value) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize none of this is duplicate code but you've got a lot of things in here with very similar names. On the encoding side:

  • EncodeFieldValueImpl
  • EncodeSubFieldValue
  • EncodeFieldEntry
  • EncodeFieldValue

Perhaps EncodeFieldEntry is actually about maps? We need better names in here to keep these straight.

Impl vs not Impl rarely helps the reader understand what's going on. Maybe EncodeFieldValueToStream?

Alternatively, should we reserve Encode for creating nanopb struct things and come up with some other verb for writing into streams? This would also be somewhat easier to comprehend if these weren't all free functions in the same file.

Given what I can see here so far, my suggestion would be to put these things that operate on a pb_ostream into a class we can stack allocate where we need it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is mostly obsolete, except for this part:

Given what I can see here so far, my suggestion would be to put these things that operate on a pb_ostream into a class we can stack allocate where we need it.

which is exactly what we decided to do (as a followup.)

For now, I've:
a) Added docs to EncodeFieldsEntry.
b) Added docs to EncodeSubFieldValue and renamed to EncodeNestedFieldValue. (When refactoring, I suspect this might actually be eliminated and translated to a bool parameter since we'll need to do the same thing for all messages that could be top level messages.)

- Remove 'const' from FieldValue::object_value's value (since it's
  immutable.)
- mapValue -> map_value
- rm memset(42)
Copy link
Contributor

@var-const var-const left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To echo today's discussion, I think there's now probably enough code to start refactoring. In particular, I'd suggest adding some nicer wrappers over Nanopb structs and functions. As it stands, the C functions are used directly, resulting in a lot of low-level details spread across the functions, so I think some intermediary interface could be helpful.

* order."
* - https://developers.google.com/protocol-buffers/docs/proto#maps-features
*
* In reality, the map items are serialized in whatever order you provide them
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clarify what is doing the serialization here (libprotobuf, I understand).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably all of them. Certainly protoc. Updated docs.

* not an unordered_map) this implies ~alpha ordering. So we need to provide
* the text format input in alpha ordering for things to match up.
*
* This is... not ideal. Nothing stops libprotobuf from changing this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably a bad idea, but just to throw it out: I guess it's possible to have a bytes array for each field and assert that each one of those arrays is found within the resulting array, and that if each of those matches is removed, the array is empty:

// Pseudocode
uint8_t encoded_fields[][] = { {0xFF, 0xFF}, {0xAA, 0xAA, 0xAA}, ... };
auto result = TheEncodingFunction();
for (const auto& encoded_field : encoded_fields) {
  auto found = std::search(result.begin(), result.end(), encoded_field.begin(), encoded_field.end());
  assert(found != result.end());
  result.erase(found, found + encoded_field.size());
}
assert(result.empty());

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that might even work, though would need to be recursive to handle maps within maps. But lets try the libprotobuf solution first. :)

mapValue.fields.funcs.encode = [](pb_ostream_t* stream, const pb_field_t*,
void* const* arg) -> bool {
auto* object_value =
reinterpret_cast<const std::map<const std::string, const FieldValue>*>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. For casting void pointers, it's preferable to use static_cast instead of reinterpret_cast.
  2. Consider dereferencing the pointer to map here (there is no check for nullptr anyway) and assigning to a reference.
  3. If you also introduce an alias, it could become:
ObjectT& object_value = *static_cast<const ObjectT*>(*arg);
  1. I presume that this is dictated by Nanopb interface, but just to check, is it possible to just use a pointer to void instead of a pointer to pointer to void?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Done.
  2. Done.
  3. Deferred to chat.
  4. No, nanopb defines it as a void**. (Curiously, it used to define it as a void*, but migrated to void** to allow it to be used as an output parameter.)

reinterpret_cast<const std::map<const std::string, const FieldValue>*>(
*arg);

// Encode each FieldEntry (i.e. key value pair.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add a hyphen (key-value).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

const_cast<void*>(reinterpret_cast<const void*>(&object_value));

bool status = pb_encode_delimited(
stream, google_firestore_v1beta1_MapValue_fields, &mapValue);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the MapValue_fields constant do? Consider adding a comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nanopb's representation of the MapValue message fields. It's a normal nanopb thing that nanopb generates for every proto message. Up to this point, I've used the *_fields relatively infrequently, because I've had to serialize most things manually. But normal use of nanopb uses these extensively in exactly this manner. (As this class grows to handle messages that don't involve oneofs, these should appear more frequently.)

Best comment I can come up with is 'represents the MapValue message fields', but I think that's pretty low value. Other suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess what I'm missing is how does Nanopb figure out where one field ends and the other starts? Does each field contain a header? Or is there an exhaustive header for the whole map? Is this constant some sort of a flag, or a header, or something else?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's purposely opaque; you're just supposed to pass it in to the pb_encode* methods when serializing. But if you dig into the details a bit, it's actually an (auto-generated) array of pb_field_t's, which nanopb uses to determine the structure of all the fields in the message. User code never touches this struct.

nanopb generates one of these for each message present in the .proto files.

FIREBASE_ASSERT(result->find(fv.first) == result->end());

// Add this key,fieldvalue to the results map.
result->emplace(fv);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think emplace has any advantage over insert here, and it looks like this should use std::move.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it has any disadvantage though either, right?

"If you want insert-or-update, use operator[]. If you want insert-if-new, use emplace(). For modern C++ code, you can forget about insert()." - http://go/totw/67

re: move: done.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pretty much ignore what I wrote, since TotW of course takes priority.

Anyway, for the record, I disagree with the rationale from the URL. emplace makes it clear that the element is constructed in-place, whereas insert makes it clear that the element was fully constructed elsewhere and is being copied/moved. map is indeed a special case, but a) as is mentioned in a footnote, my_map.insert({k, v,}) isn't really worse; b) I don't find making map, where value_type is always pair, into a special case to be very helpful. E.g., in a vector:

// I presume this vector stores some numeric type. The information provided to me
// in just this line says that this vector's value_type is int or something implicitly
// convertible to int.
vec.push_back(42);
// I presume this vector does NOT store integers but something more complicated,
// e.g. it calls PackagedTask::PackagedTask(int priority). The only piece of information
// I get from this line is that this vector's value_type has a constructor taking an int.
vec.emplace_back(42);

Another angle to look at it is that insert respects, while emplace ignores explicit-ness of constructors:

struct Foo { explicit Foo(int) {} };
vector<Foo> x;
x.push_back(1); // Compilation error
x.emplace_back(1); // Ok

I understand the desire to eliminate one feature in favor of another, but for it to be worth it, IMO, it should apply uniformly, and hopefully I've argued that it's not the case for other containers, e.g. vector. Remembering that map is a special case with regards to emplace seems to me more trouble than it's worth.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ironically, TotW 112 makes pretty much the same argument about vectors. So I guess I just disagree with the author on whether special-casing std::map is worth the trouble.

// casting).
map_value.fields.funcs.decode = [](pb_istream_t* stream, const pb_field_t*,
void** arg) -> bool {
auto* result =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as in the Encode counterpart.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

map_value.fields.arg = &result;

bool status = pb_decode_delimited(
stream, google_firestore_v1beta1_MapValue_fields, &map_value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same note about a potential comment on MapValue_fields.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack


FieldValue value = DecodeSubFieldValue(stream);

return std::make_pair(key, value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think you can just return {key, value};.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


// NB: future versions of nanopb read the remaining characters out of the
// substream (and return false if that fails) as an additional safety
// check within pb_clost_string_substream. Unfortunately, that's not present
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/pb_clost/pb_close.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -267,7 +269,19 @@ FieldValue DecodeFieldValueImpl(pb_istream_t* stream) {
}
}

void EncodeSubFieldValue(pb_ostream_t* stream, const FieldValue& field_value) {
/**
* Encodes a FieldValue *and* it's length.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/it's/its

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// Use a substream to verify that a callback doesn't write more than what it
// did the first time. (Use an initializer rather than setting fields
// individually like nanopb does. This gives us a *chance* of noticing if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, then. I guess I'm making some wrong assumption about Nanopb code. I presumed it's a plain C struct, in which case it's not an error to leave out some initializers when doing aggregate initialization (the rest of the members will be value-initialized). (If the number of initializer clauses is less than the number of members...).

FIREBASE_ASSERT(result->find(fv.first) == result->end());

// Add this key,fieldvalue to the results map.
result->emplace(fv);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pretty much ignore what I wrote, since TotW of course takes priority.

Anyway, for the record, I disagree with the rationale from the URL. emplace makes it clear that the element is constructed in-place, whereas insert makes it clear that the element was fully constructed elsewhere and is being copied/moved. map is indeed a special case, but a) as is mentioned in a footnote, my_map.insert({k, v,}) isn't really worse; b) I don't find making map, where value_type is always pair, into a special case to be very helpful. E.g., in a vector:

// I presume this vector stores some numeric type. The information provided to me
// in just this line says that this vector's value_type is int or something implicitly
// convertible to int.
vec.push_back(42);
// I presume this vector does NOT store integers but something more complicated,
// e.g. it calls PackagedTask::PackagedTask(int priority). The only piece of information
// I get from this line is that this vector's value_type has a constructor taking an int.
vec.emplace_back(42);

Another angle to look at it is that insert respects, while emplace ignores explicit-ness of constructors:

struct Foo { explicit Foo(int) {} };
vector<Foo> x;
x.push_back(1); // Compilation error
x.emplace_back(1); // Ok

I understand the desire to eliminate one feature in favor of another, but for it to be worth it, IMO, it should apply uniformly, and hopefully I've argued that it's not the case for other containers, e.g. vector. Remembering that map is a special case with regards to emplace seems to me more trouble than it's worth.


// Named '..Impl' so as to not conflict with Serializer::EncodeFieldValue.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the declaration in the class shadows the declaration in the containing namespace, so the free function doesn't even participate in the overload resolution. I'm pretty sure that ::firebase::firestore::whatever::NameOfFunction should work (sorry I communicated it poorly), but if it doesn't, guess I'm wrong.

const_cast<void*>(reinterpret_cast<const void*>(&object_value));

bool status = pb_encode_delimited(
stream, google_firestore_v1beta1_MapValue_fields, &mapValue);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess what I'm missing is how does Nanopb figure out where one field ends and the other starts? Does each field contain a header? Or is there an exhaustive header for the whole map? Is this constant some sort of a flag, or a header, or something else?

break;

case FieldValue::Type::Object:
// NB: submessages use a wiretype of PB_WT_STRING
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment confused me initially, but I think I've come back.

Initially I thought this comment was trying to tell me something about how map_value was a recursive type and so this was required on any map_value nested within a map_value. However, you don't have any provision in here for doing anything different for the outer instance so I realized this must not be it.

Then I realized this must be about just generally: any message nested within another message has this wire type.

So I was going to ask for more commentary based on my initial questions, but now I think this may be overemphasizing a point. Why is this more surprising than the fact that bools are encoded as varints? This seems to be one of any number of fiddly details, any of which we'd be kinda nuts to change, and any of which, if we did change them would fail a test.

I think this comment is only worth preserving if

  • it was particularly hard to come by this knowledge (in which case I'd a link to the doc or sources that finally cleared this up)
  • I'm misreading this and it actually is situational, in which case please tell more
  • Changing this would survive the tests we have forthcoming and trigger undesirable behavior in our counterparty

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll get rid of it. I added it for myself, as I found it mildly surprising that the wiretype for strings and submessages was the same. (Unlike the fact that a varint is used for bools, since I expect bools to be represented by ints.) But now that I've played around with nanopb a bit more, it seems less surprising now then it did before, since both strings and submessages are lengths followed by some arbitrary data.

@@ -228,6 +240,7 @@ FieldValue Serializer::DecodeFieldValue(const uint8_t* bytes, size_t length) {
break;

case google_firestore_v1beta1_Value_string_value_tag:
case google_firestore_v1beta1_Value_map_value_tag:
if (wire_type != PB_WT_STRING) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it would be a good use of FIREBASE_DEV_ASSERT.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would imply a corrupt message; I'm not sure an assertion properly applies since we should be able to detect that and return an error code rather than just failing an assertion or aborting. Either way, I'm going to defer this to the error handling PR (to come shortly).

* encoding a sub/nested message, you must include the length in the
* serialization.
*
* Call this method when encoding a non top level FieldValue. Otherwise call
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems kind of oxymoronic because the reality is that outside of tests there never will be a top-level FieldValue. It seems strange to have to write two versions of EncodeFoo and EncodeNestedFoo just to have to make this work.

Meanwhile, it seems like this gunk about

  • create sizing substream
  • call some other code to actually write values
  • take the size
  • write the size
  • if the stream is a sizing substream, inform it of the size
  • otherwise
    • ensure space
    • new substream
    • call user code again to do it for real
    • adjust bytes written and propgate errors

If you took "user code" as a std::function that took a pb_ostream_t* then this whole function largely becomes an EncodeNestedMessage(pb_ostream_t* stream, std::function<void(pb_ostream_t*)>) and no longer specific to FieldValue at all.

Note: happy to address this during the second message type where you find yourself writing this triplet with different types.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I like that approach. I'll do that as soon as we have a second message like this.

}
}

FieldValue DecodeSubFieldValue(pb_istream_t* stream) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DecodeNested?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually there's nothing in here I think we need to change in this iteration. Let's refactor and iterate as we go.

LGTM

@rsgowman rsgowman merged commit 2ae36f1 into master Mar 8, 2018
@rsgowman rsgowman deleted the rsgowman/serialize_mapvalue branch March 8, 2018 16:53
zxu123 pushed a commit to zxu123/firebase-ios-sdk that referenced this pull request May 23, 2018
Import of firebase-ios-sdk from Github.

  - 07f4695 Updates Changelog for 4.5.0 release (firebase#908) by Zsika Phillip <[email protected]>
  - 2b82a2e Allow serializing arbitrary length FieldValues (firebase#889) by rsgowman <[email protected]>
  - 3e41f25 Fix test failures that occur during prod build (firebase#910) by rsgowman <[email protected]>
  - f122cf7 Fix MutationQueue issue resulting in re-sending acknowled... by Michael Lehenbauer <[email protected]>
  - 7522314 Partial wrapping of pb_ostream_t (pt2) (firebase#903) by rsgowman <[email protected]>
  - 0d3adb1 Partial wrapping of pb_ostream_t (pt1) (firebase#899) by rsgowman <[email protected]>
  - e41f4b1 Merge Release 4.10.1 into Master (firebase#896) by zxu <[email protected]>
  - 2ae36f1 [De]Serialize FieldValue map_values ("Objects") (firebase#878) by rsgowman <[email protected]>
  - 5930ad2 Factor out a universal build script (firebase#884) by Gil <[email protected]>
  - 8ef0f14 Adds Email link sign-in (firebase#882) by Paul Beusterien <[email protected]>
  - 0b8f216 Merge pull request firebase#880 from firebase/release-4.10.0 by Paul Beusterien <[email protected]>
  - 8311c64 port paths to FSTDocumentKey (firebase#877) by zxu <[email protected]>
  - 34ebf10 Add 10 second timeout waiting for connection before clien... by Michael Lehenbauer <[email protected]>
  - 1c40e7a add converters and port paths to FSTQuery (firebase#869) by zxu <[email protected]>
  - 9b5b4d8 Serialize (and deserialize) string values (firebase#864) by rsgowman <[email protected]>
  - c6f73f7 Fix the issue completion handler is not triggered in subs... by Chen Liang <[email protected]>
  - 1ecf690 Add FieldValue.boolean_value() (firebase#862) by rsgowman <[email protected]>
  - 3e7c062 replacing Auth by C++ auth implementation (firebase#802) by zxu <[email protected]>
  - 13aeb61 Eliminate TypedValue and serialize direct from FieldValue... by rsgowman <[email protected]>

ORIGINAL_AUTHOR=Gil <[email protected]>
PiperOrigin-RevId: 188809445
zxu123 pushed a commit to zxu123/firebase-ios-sdk that referenced this pull request May 23, 2018
Import of firebase-ios-sdk from Github.

  - 07f4695 Updates Changelog for 4.5.0 release (firebase#908) by Zsika Phillip <[email protected]>
  - 2b82a2e Allow serializing arbitrary length FieldValues (firebase#889) by rsgowman <[email protected]>
  - 3e41f25 Fix test failures that occur during prod build (firebase#910) by rsgowman <[email protected]>
  - f122cf7 Fix MutationQueue issue resulting in re-sending acknowled... by Michael Lehenbauer <[email protected]>
  - 7522314 Partial wrapping of pb_ostream_t (pt2) (firebase#903) by rsgowman <[email protected]>
  - 0d3adb1 Partial wrapping of pb_ostream_t (pt1) (firebase#899) by rsgowman <[email protected]>
  - e41f4b1 Merge Release 4.10.1 into Master (firebase#896) by zxu <[email protected]>
  - 2ae36f1 [De]Serialize FieldValue map_values ("Objects") (firebase#878) by rsgowman <[email protected]>
  - 5930ad2 Factor out a universal build script (firebase#884) by Gil <[email protected]>
  - 8ef0f14 Adds Email link sign-in (firebase#882) by Paul Beusterien <[email protected]>
  - 0b8f216 Merge pull request firebase#880 from firebase/release-4.10.0 by Paul Beusterien <[email protected]>
  - 8311c64 port paths to FSTDocumentKey (firebase#877) by zxu <[email protected]>
  - 34ebf10 Add 10 second timeout waiting for connection before clien... by Michael Lehenbauer <[email protected]>
  - 1c40e7a add converters and port paths to FSTQuery (firebase#869) by zxu <[email protected]>
  - 9b5b4d8 Serialize (and deserialize) string values (firebase#864) by rsgowman <[email protected]>
  - c6f73f7 Fix the issue completion handler is not triggered in subs... by Chen Liang <[email protected]>
  - 1ecf690 Add FieldValue.boolean_value() (firebase#862) by rsgowman <[email protected]>
  - 3e7c062 replacing Auth by C++ auth implementation (firebase#802) by zxu <[email protected]>
  - 13aeb61 Eliminate TypedValue and serialize direct from FieldValue... by rsgowman <[email protected]>

PiperOrigin-RevId: 188809445
zxu123 pushed a commit to zxu123/firebase-ios-sdk that referenced this pull request May 23, 2018
Import of firebase-ios-sdk from Github.

  - 07f4695 Updates Changelog for 4.5.0 release (firebase#908) by Zsika Phillip <[email protected]>
  - 2b82a2e Allow serializing arbitrary length FieldValues (firebase#889) by rsgowman <[email protected]>
  - 3e41f25 Fix test failures that occur during prod build (firebase#910) by rsgowman <[email protected]>
  - f122cf7 Fix MutationQueue issue resulting in re-sending acknowled... by Michael Lehenbauer <[email protected]>
  - 7522314 Partial wrapping of pb_ostream_t (pt2) (firebase#903) by rsgowman <[email protected]>
  - 0d3adb1 Partial wrapping of pb_ostream_t (pt1) (firebase#899) by rsgowman <[email protected]>
  - e41f4b1 Merge Release 4.10.1 into Master (firebase#896) by zxu <[email protected]>
  - 2ae36f1 [De]Serialize FieldValue map_values ("Objects") (firebase#878) by rsgowman <[email protected]>
  - 5930ad2 Factor out a universal build script (firebase#884) by Gil <[email protected]>
  - 8ef0f14 Adds Email link sign-in (firebase#882) by Paul Beusterien <[email protected]>
  - 0b8f216 Merge pull request firebase#880 from firebase/release-4.10.0 by Paul Beusterien <[email protected]>
  - 8311c64 port paths to FSTDocumentKey (firebase#877) by zxu <[email protected]>
  - 34ebf10 Add 10 second timeout waiting for connection before clien... by Michael Lehenbauer <[email protected]>
  - 1c40e7a add converters and port paths to FSTQuery (firebase#869) by zxu <[email protected]>
  - 9b5b4d8 Serialize (and deserialize) string values (firebase#864) by rsgowman <[email protected]>
  - c6f73f7 Fix the issue completion handler is not triggered in subs... by Chen Liang <[email protected]>
  - 1ecf690 Add FieldValue.boolean_value() (firebase#862) by rsgowman <[email protected]>
  - 3e7c062 replacing Auth by C++ auth implementation (firebase#802) by zxu <[email protected]>
  - 13aeb61 Eliminate TypedValue and serialize direct from FieldValue... by rsgowman <[email protected]>

ORIGINAL_AUTHOR=Gil <[email protected]>
PiperOrigin-RevId: 188809445
zxu123 pushed a commit to zxu123/firebase-ios-sdk that referenced this pull request May 23, 2018
Import of firebase-ios-sdk from Github.

  - 07f4695 Updates Changelog for 4.5.0 release (firebase#908) by Zsika Phillip <[email protected]>
  - 2b82a2e Allow serializing arbitrary length FieldValues (firebase#889) by rsgowman <rgowman>
  - 3e41f25 Fix test failures that occur during prod build (firebase#910) by rsgowman <rgowman>
  - f122cf7 Fix MutationQueue issue resulting in re-sending acknowled... by Michael Lehenbauer <[email protected]>
  - 7522314 Partial wrapping of pb_ostream_t (pt2) (firebase#903) by rsgowman <rgowman>
  - 0d3adb1 Partial wrapping of pb_ostream_t (pt1) (firebase#899) by rsgowman <rgowman>
  - e41f4b1 Merge Release 4.10.1 into Master (firebase#896) by zxu <zxu>
  - 2ae36f1 [De]Serialize FieldValue map_values ("Objects") (firebase#878) by rsgowman <rgowman>
  - 5930ad2 Factor out a universal build script (firebase#884) by Gil <mcg>
  - 8ef0f14 Adds Email link sign-in (firebase#882) by Paul Beusterien <paulbeusterien>
  - 0b8f216 Merge pull request firebase#880 from firebase/release-4.10.0 by Paul Beusterien <paulbeusterien>
  - 8311c64 port paths to FSTDocumentKey (firebase#877) by zxu <zxu>
  - 34ebf10 Add 10 second timeout waiting for connection before clien... by Michael Lehenbauer <[email protected]>
  - 1c40e7a add converters and port paths to FSTQuery (firebase#869) by zxu <zxu>
  - 9b5b4d8 Serialize (and deserialize) string values (firebase#864) by rsgowman <rgowman>
  - c6f73f7 Fix the issue completion handler is not triggered in subs... by Chen Liang <chliang>
  - 1ecf690 Add FieldValue.boolean_value() (firebase#862) by rsgowman <rgowman>
  - 3e7c062 replacing Auth by C++ auth implementation (firebase#802) by zxu <zxu>
  - 13aeb61 Eliminate TypedValue and serialize direct from FieldValue... by rsgowman <rgowman>

ORIGINAL_AUTHOR=Gil <[email protected]>
PiperOrigin-RevId: 188809445
zxu123 pushed a commit to zxu123/firebase-ios-sdk that referenced this pull request May 23, 2018
Import of firebase-ios-sdk from Github.

  - 07f4695 Updates Changelog for 4.5.0 release (firebase#908) by Zsika Phillip <[email protected]>
  - 2b82a2e Allow serializing arbitrary length FieldValues (firebase#889) by rsgowman <rgowman>
  - 3e41f25 Fix test failures that occur during prod build (firebase#910) by rsgowman <rgowman>
  - f122cf7 Fix MutationQueue issue resulting in re-sending acknowled... by Michael Lehenbauer <[email protected]>
  - 7522314 Partial wrapping of pb_ostream_t (pt2) (firebase#903) by rsgowman <rgowman>
  - 0d3adb1 Partial wrapping of pb_ostream_t (pt1) (firebase#899) by rsgowman <rgowman>
  - e41f4b1 Merge Release 4.10.1 into Master (firebase#896) by zxu <zxu>
  - 2ae36f1 [De]Serialize FieldValue map_values ("Objects") (firebase#878) by rsgowman <rgowman>
  - 5930ad2 Factor out a universal build script (firebase#884) by Gil <mcg>
  - 8ef0f14 Adds Email link sign-in (firebase#882) by Paul Beusterien <paulbeusterien>
  - 0b8f216 Merge pull request firebase#880 from firebase/release-4.10.0 by Paul Beusterien <paulbeusterien>
  - 8311c64 port paths to FSTDocumentKey (firebase#877) by zxu <zxu>
  - 34ebf10 Add 10 second timeout waiting for connection before clien... by Michael Lehenbauer <[email protected]>
  - 1c40e7a add converters and port paths to FSTQuery (firebase#869) by zxu <zxu>
  - 9b5b4d8 Serialize (and deserialize) string values (firebase#864) by rsgowman <rgowman>
  - c6f73f7 Fix the issue completion handler is not triggered in subs... by Chen Liang <chliang>
  - 1ecf690 Add FieldValue.boolean_value() (firebase#862) by rsgowman <rgowman>
  - 3e7c062 replacing Auth by C++ auth implementation (firebase#802) by zxu <zxu>
  - 13aeb61 Eliminate TypedValue and serialize direct from FieldValue... by rsgowman <rgowman>

ORIGINAL_AUTHOR=Gil <[email protected]>
PiperOrigin-RevId: 188809445
zxu123 pushed a commit to zxu123/firebase-ios-sdk that referenced this pull request May 23, 2018
--
197713382 by zxu <zxu>:

Implement more on listener class and implement ListenerRegistration.

TESTED:
blaze test //sensored/testapp:testapp_build_test
blaze test //sensored/testapp:testapp_build_test --config=android_x86
blaze test //sensored/testapp:android_testapp_test --config=android_x86 --define firebase_build=stable
blaze test //sensored/cpp:firestore_kokoro
blaze test //sensored/cpp:listener_registration_test
--
196551381 by zxu <zxu>:

Implement more on listener class and implement the DocumentReference::AddSnapshotListener.

Also added a few test case and fixed a bug in DocumentReferenceInternal.

ListenerRegistration will be implemented in subsequent CL.

TESTED:
blaze test //sensored/testapp:testapp_build_test
blaze test //sensored/testapp:testapp_build_test --config=android_x86
blaze test //sensored/testapp:android_testapp_test --config=android_x86 --define firebase_build=stable
blaze test //sensored/cpp:firestore_kokoro
blaze test //sensored/cpp:firestore_test
--
196276752 by zxu <zxu>:

Implement the SnapshotMetadata with inline methods and (non-)Wrapper for Firestore C++.

TESTED:
blaze test //sensored/testapp:testapp_build_test
blaze test //sensored/testapp:testapp_build_test --config=android_x86
blaze test //sensored/testapp:android_testapp_test --config=android_x86 --experimental_one_version_enforcement=warning
blaze test //sensored/cpp:firestore_kokoro
blaze test //sensored/cpp:firestore_test
--
195841793 by zxu <zxu>:

Implement the wrapper class for callback (EventListener).

People unlike huge CL. So sending this short one for review. In subsequent CLs, I'll do:
*   cache/uncache or the CppEventListener class and the wiring of native method;
*   actually using this to implement DocumentReference::AddSnapshotListener;
*   update the testapp to run android_test with callback.
--
194112388 by zxu <zxu>:

Add Android-Wrapper for DocumentReference's non-callback methods.

Tested:
blaze test //sensored/testapp:testapp_build_test
blaze test //sensored/testapp:testapp_build_test --config=android_x86
blaze test //sensored/cpp:firestore_kokoro
blaze test //sensored/cpp:firestore_test
--
192445183 by zxu <zxu>:

Add Android-Wrapper for Firestore's remaining methods.
Tested:
blaze test //sensored/testapp:testapp_build_test
blaze test //sensored/testapp:testapp_build_test --config=android_x86
blaze test //sensored/cpp:firestore_kokoro
blaze test //sensored/cpp:firestore_test
--
190986604 by mcg <mcg>:

Manually import the public portion of

  - 22c226a C++ migration: make Timestamp class a part of public API ... by Konstantin Varlamov <[email protected]>

Note that this only imports the header, which otherewise won't come over with a
regular copybara run because we're currently excluding the public headers from
the Firestore import with cl/188810622.

The .cc implementation of this will come over with a regular copybara import,
coming shortly.
--
189013767 by zxu <zxu>:

Add Android-Wrapper for Firestore's method that does not involve other Firestore types.
Tested:
blaze test //sensored/testapp:testapp_build_test
blaze test //sensored/testapp:testapp_build_test --config=android_x86
blaze test //sensored/cpp:firestore_kokoro
--
188809445 by mcg <mcg>:

Import of firebase-ios-sdk from Github.

  - 07f4695 Updates Changelog for 4.5.0 release (firebase#908) by Zsika Phillip <[email protected]>
  - 2b82a2e Allow serializing arbitrary length FieldValues (firebase#889) by rsgowman <rgowman>
  - 3e41f25 Fix test failures that occur during prod build (firebase#910) by rsgowman <rgowman>
  - f122cf7 Fix MutationQueue issue resulting in re-sending acknowled... by Michael Lehenbauer <[email protected]>
  - 7522314 Partial wrapping of pb_ostream_t (pt2) (firebase#903) by rsgowman <rgowman>
  - 0d3adb1 Partial wrapping of pb_ostream_t (pt1) (firebase#899) by rsgowman <rgowman>
  - e41f4b1 Merge Release 4.10.1 into Master (firebase#896) by zxu <zxu>
  - 2ae36f1 [De]Serialize FieldValue map_values ("Objects") (firebase#878) by rsgowman <rgowman>
  - 5930ad2 Factor out a universal build script (firebase#884) by Gil <mcg>
  - 8ef0f14 Adds Email link sign-in (firebase#882) by Paul Beusterien <paulbeusterien>
  - 0b8f216 Merge pull request firebase#880 from firebase/release-4.10.0 by Paul Beusterien <paulbeusterien>
  - 8311c64 port paths to FSTDocumentKey (firebase#877) by zxu <zxu>
  - 34ebf10 Add 10 second timeout waiting for connection before clien... by Michael Lehenbauer <[email protected]>
  - 1c40e7a add converters and port paths to FSTQuery (firebase#869) by zxu <zxu>
  - 9b5b4d8 Serialize (and deserialize) string values (firebase#864) by rsgowman <rgowman>
  - c6f73f7 Fix the issue completion handler is not triggered in subs... by Chen Liang <chliang>
  - 1ecf690 Add FieldValue.boolean_value() (firebase#862) by rsgowman <rgowman>
  - 3e7c062 replacing Auth by C++ auth implementation (firebase#802) by zxu <zxu>
  - 13aeb61 Eliminate TypedValue and serialize direct from FieldValue... by rsgowman <rgowman>
--
187049498 by mcg <mcg>:

Import of firebase-ios-sdk from Github.

  - f7d9f29 Fix lint warnings (firebase#840) by Gil <mcg>
  - b5c60ad Refactor [En|De]codeVarint to be symetric wrt tags (firebase#837) by rsgowman <rgowman>
  - ddc12c0 Merge pull request firebase#694 from morganchen12/auth-docs by Morgan Chen <[email protected]>
  - 442d46f Avoid warnings about failing to override a designated ini... by Gil <mcg>
  - 4dc63f8 Fix Firestore tests for M22 (firebase#834) by Gil <mcg>
  - 935f3ca  Avoid wrapping and rewrapping NSStrings when constructin... by Gil <mcg>
  - 6ce954a Serialize (and deserialize) int64 values (firebase#818) (firebase#829) by rsgowman <rgowman>
  - 41dcd4b Fix trivial mem leak in the test suite (firebase#828) by rsgowman <rgowman>
  - 50f9df9 Accept FIRTimestamp where NSDate is currently accepted as... by Konstantin Varlamov <[email protected]>
  - 67b068e Fix implicit retain self warnings (firebase#808) by rsgowman <rgowman>
  - 14ea068 Make FSTTimestamp into a public Firestore class (firebase#698) by Konstantin Varlamov <[email protected]>
  - de00de2 [En|De]codeUnsignedVarint -> [En|De]codeVarint (firebase#817) by rsgowman <rgowman>
  - 9bf73ab Fix two stream close issues (b/73167987, b/73382103). (firebase#8... by Michael Lehenbauer <[email protected]>
  - 7a4a2ea replacing FSTGetTokenResult by C++ Token implementation (... by zxu <zxu>
  - a9f3f35 Delete stale Firestore instances after FIRApp is deleted.... by Ryan Wilson <wilsonryan>
  - aa6f1ae Disable -Wrange-loop-analysis for abseil (firebase#807) by rsgowman <rgowman>
  - ef55eef Require official 1.4.0 for min CocoaPods version (firebase#806) by Paul Beusterien <paulbeusterien>
  - 7cddb97 Serialize (and deserialize) bool values (firebase#791) by rsgowman <rgowman>
  - 7a97f6c Enable -Wcomma for our build; disable it for abseil. (firebase#799) by rsgowman <rgowman>
  - 81ee594 DispatchQueue delayed callback improvements + testing (firebase#7... by Michael Lehenbauer <[email protected]>
  - fd9fd27 replacing Auth/FSTUser by C++ auth implementation (firebase#804) by zxu <zxu>
  - 6889850 Merge pull request firebase#801 from firebase/release-4.9.0 by Paul Beusterien <paulbeusterien>
  - e4be254 Fixed capitalization of init in test function. (firebase#797) by Ryan Wilson <wilsonryan>
  - 933be73 Improve documentation on auto-init property in FCM. (firebase#792) by Chen Liang <chliang>
  - 0f3c24b Actually ignore events on inactive streams, rather than j... by Greg Soltis <gsoltis>
  - fe19fca Serialize and deserialize null (firebase#783) by rsgowman <rgowman>
  - 95d0411 fix flaky test (firebase#788) by zxu <zxu>
  - b6613bd Cleaning up implicit retain for the RTDB and Storage by Sebastian Schmidt <mrschmidt>
  - 5f5f808 Keep track of number of queries in the query cache (firebase#776) by Greg Soltis <gsoltis>
  - c7c51a7 Fix double parsing on 32 bit devices. (firebase#787) by Ryan Wilson <wilsonryan>
  - 9504582 Port Firestore Document to C++ (firebase#777) by zxu <zxu>
  - adf9fb3 Update FieldValue of type Reference (firebase#775) by zxu <zxu>
  - 4afcfb1 Move to explicit nil check. (firebase#782) by Ryan Wilson <wilsonryan>
  - fd83e07 Strawman C++ API (firebase#763) by rsgowman <rgowman>
  - 8003348 C++ port: add C++ equivalent of FSTDocumentKey. (firebase#762) by Konstantin Varlamov <[email protected]>
  - 612d99c Fix Core CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings (#... by Paul Beusterien <paulbeusterien>
  - bf45a15 port Firestore SnapshotVersion in C++ (firebase#767) by zxu <zxu>
  - d70c23e port Firestore Auth module in C++ (firebase#733) by zxu <zxu>
  - 633eb7b Let Travis run for `CMake` test and `lint.sh` (firebase#769) by zxu <zxu>
  - 274fe52 cmake build fixes (firebase#770) by rsgowman <rgowman>
--
184568931 by mcg <mcg>:

Import of firebase-ios-sdk from Github.

  - 32266c5 Make sure Firestore/core/include is in the podspec (firebase#748) by Gil <mcg>
  - 070c0ea Merge pull request firebase#746 from morganchen12/unguarded-block by Morgan Chen <[email protected]>
  - 13f572e Increase expectation timeout to 25 seconds. (firebase#744) by Michael Lehenbauer <[email protected]>
  - 47d81fd fix (firebase#739) by Chen Liang <chliang>
  - 515625c Remove predecessorKey,Object,Document, etc (firebase#735) by Gil <mcg>
  - ac30522 Start on ArraySortedMap in C++ (firebase#721) by Gil <mcg>
  - 729b8d1 Move all Firestore Objective-C to Objective-C++ (firebase#734) by Gil <mcg>
  - 693d064 Merge pull request firebase#732 from OleksiyA/FCM-subscribe-compl... by Oleksiy Ivanov <[email protected]>
  - 3cbdbf2 Schema migrations for LevelDB (firebase#728) by Greg Soltis <gsoltis>
  - 6474a82 Firestore DatabaseId in C++ (firebase#727) by zxu <zxu>
  - 05ef5ae Add absl_strings to firebase_firestore_util_test dependen... by rsgowman <rgowman>
  - 03a0069 Add changelog entry for my last PR (oops) and also add a ... by Michael Lehenbauer <[email protected]>
  - 5a280ca  Import iterator_adaptors from google3 (firebase#718) by Gil <mcg>
  - 63a380b Use fixed-sized types (firebase#719) by Gil <mcg>
  - 6dc1373 Version updates to 4.8.2 (firebase#722) by Paul Beusterien <paulbeusterien>
  - a74d39f Fix a number of c++ build errors (firebase#715) by rsgowman <rgowman>
  - cceeec3 travis: check for copyright in sources (firebase#717) by Paul Beusterien <paulbeusterien>
  - 4d7c973 Fix b/72502745: OnlineState changes cause limbo document ... by Michael Lehenbauer <[email protected]>
  - af6976a normalize and port the rest of Firebase/Port code (firebase#713) by zxu <zxu>
  - 7226b4a port TargetIdGenerator to iOS (firebase#709) by zxu <zxu>
  - 5306474 adding unit test for auto init enable function (firebase#710) by Chen Liang <chliang>
  - 821fb90 implement `TargetIdGenerator` in C++ for Firestore (firebase#701) by zxu <zxu>
  - 5fdda3f normalize string_util (firebase#708) by zxu <zxu>
  - 15a2926 Update CHANGELOG for M21.1 release (firebase#679) by Ryan Wilson <wilsonryan>
  - f027214 Fix incorrect deprecation message (firebase#688) by Iulian Onofrei <[email protected]>
  - bfa0e40 Implement the rest of FieldValue types for C++ (firebase#687) by zxu <zxu>
  - 1f77212 Properly publish Abseil sources as a part of the podspec ... by Gil <mcg>

ORIGINAL_AUTHOR=Firebase <firebase-noreply>
PiperOrigin-RevId: 197713382
minafarid pushed a commit to minafarid/firebase-ios-sdk that referenced this pull request Jun 6, 2018
These can (recursively) contain other FieldValues.
@firebase firebase locked and limited conversation to collaborators Nov 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants