Skip to content

Commit 378f4db

Browse files
authored
Add support to [de]serialize missing FV types to the remote serializer (#2588)
Add support for [de]serializing double/blob/GeoPoint/Array FieldValues
1 parent c4527b1 commit 378f4db

File tree

4 files changed

+287
-28
lines changed

4 files changed

+287
-28
lines changed

Firestore/core/src/firebase/firestore/model/field_value.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ class FieldValue {
117117
return integer_value_;
118118
}
119119

120+
double double_value() const {
121+
HARD_ASSERT(tag_ == Type::Double);
122+
return double_value_;
123+
}
124+
120125
Timestamp timestamp_value() const {
121126
HARD_ASSERT(tag_ == Type::Timestamp);
122127
return *timestamp_value_;
@@ -127,6 +132,21 @@ class FieldValue {
127132
return *string_value_;
128133
}
129134

135+
const std::vector<uint8_t>& blob_value() const {
136+
HARD_ASSERT(tag_ == Type::Blob);
137+
return *blob_value_;
138+
}
139+
140+
const GeoPoint& geo_point_value() const {
141+
HARD_ASSERT(tag_ == Type::GeoPoint);
142+
return *geo_point_value_;
143+
}
144+
145+
const std::vector<FieldValue>& array_value() const {
146+
HARD_ASSERT(tag_ == Type::Array);
147+
return *array_value_;
148+
}
149+
130150
/** factory methods. */
131151
static FieldValue Null();
132152
static FieldValue True();

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

Lines changed: 119 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -286,38 +286,62 @@ google_firestore_v1_Value Serializer::EncodeFieldValue(
286286
case FieldValue::Type::Null:
287287
result.which_value_type = google_firestore_v1_Value_null_value_tag;
288288
result.null_value = google_protobuf_NullValue_NULL_VALUE;
289-
break;
289+
return result;
290290

291291
case FieldValue::Type::Boolean:
292292
result.which_value_type = google_firestore_v1_Value_boolean_value_tag;
293293
result.boolean_value = field_value.boolean_value();
294-
break;
294+
return result;
295295

296296
case FieldValue::Type::Integer:
297297
result.which_value_type = google_firestore_v1_Value_integer_value_tag;
298298
result.integer_value = field_value.integer_value();
299-
break;
299+
return result;
300300

301-
case FieldValue::Type::String:
302-
result.which_value_type = google_firestore_v1_Value_string_value_tag;
303-
result.string_value = EncodeString(field_value.string_value());
304-
break;
301+
case FieldValue::Type::Double:
302+
result.which_value_type = google_firestore_v1_Value_double_value_tag;
303+
result.double_value = field_value.double_value();
304+
return result;
305305

306306
case FieldValue::Type::Timestamp:
307307
result.which_value_type = google_firestore_v1_Value_timestamp_value_tag;
308308
result.timestamp_value = EncodeTimestamp(field_value.timestamp_value());
309-
break;
309+
return result;
310+
311+
case FieldValue::Type::ServerTimestamp:
312+
// TODO(rsgowman): Implement
313+
abort();
314+
315+
case FieldValue::Type::String:
316+
result.which_value_type = google_firestore_v1_Value_string_value_tag;
317+
result.string_value = EncodeString(field_value.string_value());
318+
return result;
319+
320+
case FieldValue::Type::Blob:
321+
result.which_value_type = google_firestore_v1_Value_bytes_value_tag;
322+
result.bytes_value = EncodeBytes(field_value.blob_value());
323+
return result;
324+
325+
case FieldValue::Type::Reference:
326+
// TODO(rsgowman): Implement
327+
abort();
328+
329+
case FieldValue::Type::GeoPoint:
330+
result.which_value_type = google_firestore_v1_Value_geo_point_value_tag;
331+
result.geo_point_value = EncodeGeoPoint(field_value.geo_point_value());
332+
return result;
333+
334+
case FieldValue::Type::Array:
335+
result.which_value_type = google_firestore_v1_Value_array_value_tag;
336+
result.array_value = EncodeArray(field_value.array_value());
337+
return result;
310338

311339
case FieldValue::Type::Object:
312340
result.which_value_type = google_firestore_v1_Value_map_value_tag;
313341
result.map_value = EncodeMapValue(ObjectValue(field_value));
314-
break;
315-
316-
default:
317-
// TODO(rsgowman): implement the other types
318-
abort();
342+
return result;
319343
}
320-
return result;
344+
UNREACHABLE();
321345
}
322346

323347
FieldValue Serializer::DecodeFieldValue(Reader* reader,
@@ -342,27 +366,38 @@ FieldValue Serializer::DecodeFieldValue(Reader* reader,
342366
case google_firestore_v1_Value_integer_value_tag:
343367
return FieldValue::FromInteger(msg.integer_value);
344368

345-
case google_firestore_v1_Value_string_value_tag:
346-
return FieldValue::FromString(DecodeString(msg.string_value));
369+
case google_firestore_v1_Value_double_value_tag:
370+
return FieldValue::FromDouble(msg.double_value);
347371

348372
case google_firestore_v1_Value_timestamp_value_tag: {
349373
return FieldValue::FromTimestamp(
350374
DecodeTimestamp(reader, msg.timestamp_value));
351375
}
352376

353-
case google_firestore_v1_Value_map_value_tag: {
354-
return FieldValue::FromMap(DecodeMapValue(reader, msg.map_value));
377+
case google_firestore_v1_Value_string_value_tag:
378+
return FieldValue::FromString(DecodeString(msg.string_value));
379+
380+
case google_firestore_v1_Value_bytes_value_tag: {
381+
std::vector<uint8_t> bytes = DecodeBytes(msg.bytes_value);
382+
return FieldValue::FromBlob(bytes.data(), bytes.size());
355383
}
356384

357-
case google_firestore_v1_Value_double_value_tag:
358-
case google_firestore_v1_Value_bytes_value_tag:
359385
case google_firestore_v1_Value_reference_value_tag:
360-
case google_firestore_v1_Value_geo_point_value_tag:
361-
case google_firestore_v1_Value_array_value_tag:
362386
// TODO(b/74243929): Implement remaining types.
363387
HARD_FAIL("Unhandled message field number (tag): %i.",
364388
msg.which_value_type);
365389

390+
case google_firestore_v1_Value_geo_point_value_tag:
391+
return FieldValue::FromGeoPoint(
392+
DecodeGeoPoint(reader, msg.geo_point_value));
393+
394+
case google_firestore_v1_Value_array_value_tag:
395+
return FieldValue::FromArray(DecodeArray(reader, msg.array_value));
396+
397+
case google_firestore_v1_Value_map_value_tag: {
398+
return FieldValue::FromMap(DecodeMapValue(reader, msg.map_value));
399+
}
400+
366401
default:
367402
reader->Fail(StringFormat("Invalid type while decoding FieldValue: %s",
368403
msg.which_value_type));
@@ -667,7 +702,7 @@ google_firestore_v1_DocumentMask Serializer::EncodeDocumentMask(
667702
google_firestore_v1_DocumentMask result{};
668703

669704
size_t count = mask.size();
670-
HARD_ASSERT(count <= std::numeric_limits<pb_size_t>::max(),
705+
HARD_ASSERT(count <= PB_SIZE_MAX,
671706
"Unable to encode specified document mask. Too many fields.");
672707
result.field_paths_count = static_cast<pb_size_t>(count);
673708
result.field_paths = MakeArray<pb_bytes_array_t*>(count);
@@ -816,7 +851,7 @@ Timestamp Serializer::DecodeTimestamp(
816851
reader->Fail(
817852
"Invalid message: timestamp beyond the earliest supported date");
818853
} else if (TimestampInternal::Max().seconds() < timestamp_proto.seconds) {
819-
reader->Fail("Invalid message: timestamp behond the latest supported date");
854+
reader->Fail("Invalid message: timestamp beyond the latest supported date");
820855
} else if (timestamp_proto.nanos < 0 || timestamp_proto.nanos > 999999999) {
821856
reader->Fail(
822857
"Invalid message: timestamp nanos must be between 0 and 999999999");
@@ -826,6 +861,66 @@ Timestamp Serializer::DecodeTimestamp(
826861
return Timestamp{timestamp_proto.seconds, timestamp_proto.nanos};
827862
}
828863

864+
/* static */
865+
google_type_LatLng Serializer::EncodeGeoPoint(const GeoPoint& geo_point_value) {
866+
google_type_LatLng result{};
867+
result.latitude = geo_point_value.latitude();
868+
result.longitude = geo_point_value.longitude();
869+
return result;
870+
}
871+
872+
/* static */
873+
GeoPoint Serializer::DecodeGeoPoint(nanopb::Reader* reader,
874+
const google_type_LatLng& latlng_proto) {
875+
// The GeoPoint ctor will assert if we provide values outside the valid range.
876+
// However, since we're decoding, a single corrupt byte could cause this to
877+
// occur, so we'll verify the ranges before passing them in since we'd rather
878+
// not abort in these situations.
879+
double latitude = latlng_proto.latitude;
880+
double longitude = latlng_proto.longitude;
881+
if (std::isnan(latitude) || latitude < -90 || 90 < latitude) {
882+
reader->Fail("Invalid message: Latitude must be in the range of [-90, 90]");
883+
} else if (std::isnan(longitude) || longitude < -180 || 180 < longitude) {
884+
reader->Fail(
885+
"Invalid message: Latitude must be in the range of [-180, 180]");
886+
}
887+
888+
if (!reader->status().ok()) return GeoPoint();
889+
return GeoPoint(latitude, longitude);
890+
}
891+
892+
/* static */
893+
google_firestore_v1_ArrayValue Serializer::EncodeArray(
894+
const std::vector<FieldValue>& array_value) {
895+
google_firestore_v1_ArrayValue result{};
896+
897+
size_t count = array_value.size();
898+
HARD_ASSERT(count <= PB_SIZE_MAX,
899+
"Unable to encode specified array. Too many entries.");
900+
result.values_count = count;
901+
result.values = MakeArray<google_firestore_v1_Value>(count);
902+
903+
size_t i = 0;
904+
for (const FieldValue& fv : array_value) {
905+
result.values[i++] = EncodeFieldValue(fv);
906+
}
907+
908+
return result;
909+
}
910+
911+
/* static */
912+
std::vector<FieldValue> Serializer::DecodeArray(
913+
nanopb::Reader* reader, const google_firestore_v1_ArrayValue& array_proto) {
914+
std::vector<FieldValue> result;
915+
result.reserve(array_proto.values_count);
916+
917+
for (size_t i = 0; i < array_proto.values_count; i++) {
918+
result.push_back(DecodeFieldValue(reader, array_proto.values[i]));
919+
}
920+
921+
return result;
922+
}
923+
829924
} // namespace remote
830925
} // namespace firestore
831926
} // namespace firebase

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
2727
#include "Firestore/Protos/nanopb/google/firestore/v1/firestore.nanopb.h"
28+
#include "Firestore/Protos/nanopb/google/type/latlng.nanopb.h"
2829
#include "Firestore/core/src/firebase/firestore/core/query.h"
2930
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
3031
#include "Firestore/core/src/firebase/firestore/model/document.h"
@@ -210,6 +211,16 @@ class Serializer {
210211
nanopb::Reader* reader,
211212
const google_firestore_v1_Target_QueryTarget& proto);
212213

214+
static google_type_LatLng EncodeGeoPoint(const GeoPoint& geo_point_value);
215+
static GeoPoint DecodeGeoPoint(nanopb::Reader* reader,
216+
const google_type_LatLng& latlng_proto);
217+
218+
static google_firestore_v1_ArrayValue EncodeArray(
219+
const std::vector<model::FieldValue>& array_value);
220+
static std::vector<model::FieldValue> DecodeArray(
221+
nanopb::Reader* reader,
222+
const google_firestore_v1_ArrayValue& array_proto);
223+
213224
private:
214225
std::unique_ptr<model::Document> DecodeFoundDocument(
215226
nanopb::Reader* reader,

0 commit comments

Comments
 (0)