Skip to content

Commit 6ce954a

Browse files
authored
Serialize (and deserialize) int64 values (firebase#818) (firebase#829)
1 parent 41dcd4b commit 6ce954a

File tree

5 files changed

+109
-8
lines changed

5 files changed

+109
-8
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ namespace {
4545
*/
4646
bool Comparable(Type lhs, Type rhs) {
4747
switch (lhs) {
48-
case Type::Long:
48+
case Type::Integer:
4949
case Type::Double:
50-
return rhs == Type::Long || rhs == Type::Double;
50+
return rhs == Type::Integer || rhs == Type::Double;
5151
case Type::Timestamp:
5252
case Type::ServerTimestamp:
5353
return rhs == Type::Timestamp || rhs == Type::ServerTimestamp;
@@ -78,7 +78,7 @@ FieldValue& FieldValue::operator=(const FieldValue& value) {
7878
case Type::Boolean:
7979
boolean_value_ = value.boolean_value_;
8080
break;
81-
case Type::Long:
81+
case Type::Integer:
8282
integer_value_ = value.integer_value_;
8383
break;
8484
case Type::Double:
@@ -179,7 +179,7 @@ const FieldValue& FieldValue::NanValue() {
179179

180180
FieldValue FieldValue::IntegerValue(int64_t value) {
181181
FieldValue result;
182-
result.SwitchTo(Type::Long);
182+
result.SwitchTo(Type::Integer);
183183
result.integer_value_ = value;
184184
return result;
185185
}
@@ -304,8 +304,8 @@ bool operator<(const FieldValue& lhs, const FieldValue& rhs) {
304304
return false;
305305
case Type::Boolean:
306306
return Comparator<bool>()(lhs.boolean_value_, rhs.boolean_value_);
307-
case Type::Long:
308-
if (rhs.type() == Type::Long) {
307+
case Type::Integer:
308+
if (rhs.type() == Type::Integer) {
309309
return Comparator<int64_t>()(lhs.integer_value_, rhs.integer_value_);
310310
} else {
311311
return util::CompareMixedNumber(rhs.double_value_,

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
2929
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
3030
#include "Firestore/core/src/firebase/firestore/model/timestamp.h"
31+
#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
3132

3233
namespace firebase {
3334
namespace firestore {
@@ -63,7 +64,7 @@ class FieldValue {
6364
enum class Type {
6465
Null, // Null
6566
Boolean, // Boolean
66-
Long, // Number type starts here
67+
Integer, // Number type starts here
6768
Double,
6869
Timestamp, // Timestamp type starts here
6970
ServerTimestamp,
@@ -95,6 +96,11 @@ class FieldValue {
9596
return tag_;
9697
}
9798

99+
int64_t integer_value() const {
100+
FIREBASE_ASSERT(tag_ == Type::Integer);
101+
return integer_value_;
102+
}
103+
98104
/** factory methods. */
99105
static const FieldValue& NullValue();
100106
static const FieldValue& TrueValue();

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ bool DecodeBool(pb_istream_t* stream) {
9898
}
9999
}
100100

101+
void EncodeInteger(pb_ostream_t* stream, int64_t integer_value) {
102+
return EncodeVarint(stream, google_firestore_v1beta1_Value_integer_value_tag,
103+
integer_value);
104+
}
105+
106+
int64_t DecodeInteger(pb_istream_t* stream) {
107+
return DecodeVarint(stream);
108+
}
109+
101110
} // namespace
102111

103112
using firebase::firestore::model::FieldValue;
@@ -118,6 +127,9 @@ Serializer::TypedValue Serializer::EncodeFieldValue(
118127
proto_value.value.boolean_value = false;
119128
}
120129
break;
130+
case FieldValue::Type::Integer:
131+
proto_value.value.integer_value = field_value.integer_value();
132+
break;
121133
default:
122134
// TODO(rsgowman): implement the other types
123135
abort();
@@ -141,6 +153,10 @@ void Serializer::EncodeTypedValue(const TypedValue& value,
141153
EncodeBool(&stream, value.value.boolean_value);
142154
break;
143155

156+
case FieldValue::Type::Integer:
157+
EncodeInteger(&stream, value.value.integer_value);
158+
break;
159+
144160
default:
145161
// TODO(rsgowman): implement the other types
146162
abort();
@@ -156,6 +172,8 @@ FieldValue Serializer::DecodeFieldValue(
156172
return FieldValue::NullValue();
157173
case FieldValue::Type::Boolean:
158174
return FieldValue::BooleanValue(value_proto.value.boolean_value);
175+
case FieldValue::Type::Integer:
176+
return FieldValue::IntegerValue(value_proto.value.integer_value);
159177
default:
160178
// TODO(rsgowman): implement the other types
161179
abort();
@@ -185,6 +203,10 @@ Serializer::TypedValue Serializer::DecodeTypedValue(const uint8_t* bytes,
185203
result.type = FieldValue::Type::Boolean;
186204
result.value.boolean_value = DecodeBool(&stream);
187205
break;
206+
case google_firestore_v1beta1_Value_integer_value_tag:
207+
result.type = FieldValue::Type::Integer;
208+
result.value.integer_value = DecodeInteger(&stream);
209+
break;
188210

189211
default:
190212
// TODO(rsgowman): figure out error handling
@@ -209,6 +231,8 @@ bool operator==(const Serializer::TypedValue& lhs,
209231
return true;
210232
case FieldValue::Type::Boolean:
211233
return lhs.value.boolean_value == rhs.value.boolean_value;
234+
case FieldValue::Type::Integer:
235+
return lhs.value.integer_value == rhs.value.integer_value;
212236
default:
213237
// TODO(rsgowman): implement the other types
214238
abort();

Firestore/core/test/firebase/firestore/model/field_value_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ TEST(FieldValue, NumberType) {
5757
const FieldValue integer_value = FieldValue::IntegerValue(10L);
5858
const FieldValue double_value = FieldValue::DoubleValue(10.1);
5959
EXPECT_EQ(Type::Double, nan_value.type());
60-
EXPECT_EQ(Type::Long, integer_value.type());
60+
EXPECT_EQ(Type::Integer, integer_value.type());
6161
EXPECT_EQ(Type::Double, double_value.type());
6262
EXPECT_TRUE(nan_value < integer_value);
6363
EXPECT_TRUE(nan_value < double_value);

Firestore/core/test/firebase/firestore/remote/serializer_test.cc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#include <pb.h>
4040
#include <pb_encode.h>
41+
#include <limits>
4142
#include <vector>
4243

4344
#include "Firestore/core/src/firebase/firestore/model/field_value.h"
@@ -134,6 +135,76 @@ TEST_F(SerializerTest, EncodesBoolProtoToBytes) {
134135
}
135136
}
136137

138+
TEST_F(SerializerTest, EncodesIntegersModelToProto) {
139+
std::vector<int64_t> testCases{0,
140+
1,
141+
-1,
142+
100,
143+
-100,
144+
std::numeric_limits<int64_t>::min(),
145+
std::numeric_limits<int64_t>::max()};
146+
for (int64_t test : testCases) {
147+
FieldValue model = FieldValue::IntegerValue(test);
148+
Serializer::TypedValue proto{FieldValue::Type::Integer,
149+
google_firestore_v1beta1_Value_init_default};
150+
proto.value.integer_value = test;
151+
ExpectRoundTrip(model, proto, FieldValue::Type::Integer);
152+
}
153+
}
154+
155+
TEST_F(SerializerTest, EncodesIntegersProtoToBytes) {
156+
// NB: because we're calculating the bytes via protoc (instead of
157+
// libprotobuf) we have to look at values from numeric_limits<T> ahead of
158+
// time. So these test cases make the following assumptions about
159+
// numeric_limits: (linking libprotobuf is starting to sound like a better
160+
// idea. :)
161+
// -9223372036854775808
162+
// == -8000000000000000
163+
// == numeric_limits<int64_t>::min()
164+
// 9223372036854775807
165+
// == 7FFFFFFFFFFFFFFF
166+
// == numeric_limits<int64_t>::max()
167+
//
168+
// For now, lets at least verify these values:
169+
EXPECT_EQ(-9223372036854775807 - 1, std::numeric_limits<int64_t>::min());
170+
EXPECT_EQ(9223372036854775807, std::numeric_limits<int64_t>::max());
171+
172+
struct TestCase {
173+
int64_t value;
174+
std::vector<uint8_t> bytes;
175+
};
176+
177+
std::vector<TestCase> cases{
178+
// TEXT_FORMAT_PROTO: 'integer_value: 0'
179+
TestCase{0, {0x10, 0x00}},
180+
// TEXT_FORMAT_PROTO: 'integer_value: 1'
181+
TestCase{1, {0x10, 0x01}},
182+
// TEXT_FORMAT_PROTO: 'integer_value: -1'
183+
TestCase{
184+
-1,
185+
{0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}},
186+
// TEXT_FORMAT_PROTO: 'integer_value: 100'
187+
TestCase{100, {0x10, 0x64}},
188+
// TEXT_FORMAT_PROTO: 'integer_value: -100'
189+
TestCase{
190+
-100,
191+
{0x10, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}},
192+
// TEXT_FORMAT_PROTO: 'integer_value: -9223372036854775808'
193+
TestCase{
194+
-9223372036854775807 - 1,
195+
{0x10, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01}},
196+
// TEXT_FORMAT_PROTO: 'integer_value: 9223372036854775807'
197+
TestCase{9223372036854775807,
198+
{0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}}};
199+
200+
for (const TestCase& test : cases) {
201+
Serializer::TypedValue proto{FieldValue::Type::Integer,
202+
google_firestore_v1beta1_Value_init_default};
203+
proto.value.integer_value = test.value;
204+
ExpectRoundTrip(proto, test.bytes, FieldValue::Type::Integer);
205+
}
206+
}
207+
137208
// TODO(rsgowman): Test [en|de]coding multiple protos into the same output
138209
// vector.
139210

0 commit comments

Comments
 (0)