aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/core/src/firebase/firestore/remote/serializer.cc
blob: c6c699f8c8ff5cf626149670111b1a83edf59936 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
 * Copyright 2018 Google
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Firestore/core/src/firebase/firestore/remote/serializer.h"

#include <pb_decode.h>
#include <pb_encode.h>

namespace firebase {
namespace firestore {
namespace remote {

namespace {

void EncodeUnsignedVarint(pb_ostream_t* stream,
                          uint32_t field_number,
                          uint64_t value) {
  bool status = pb_encode_tag(stream, PB_WT_VARINT, field_number);
  if (!status) {
    // TODO(rsgowman): figure out error handling
    abort();
  }

  status = pb_encode_varint(stream, value);
  if (!status) {
    // TODO(rsgowman): figure out error handling
    abort();
  }
}

uint64_t DecodeUnsignedVarint(pb_istream_t* stream) {
  uint64_t varint_value;
  bool status = pb_decode_varint(stream, &varint_value);
  if (!status) {
    // TODO(rsgowman): figure out error handling
    abort();
  }
  return varint_value;
}

void EncodeNull(pb_ostream_t* stream) {
  return EncodeUnsignedVarint(stream,
                              google_firestore_v1beta1_Value_null_value_tag,
                              google_protobuf_NullValue_NULL_VALUE);
}

void DecodeNull(pb_istream_t* stream) {
  uint64_t varint = DecodeUnsignedVarint(stream);
  if (varint != google_protobuf_NullValue_NULL_VALUE) {
    // TODO(rsgowman): figure out error handling
    abort();
  }
}

void EncodeBool(pb_ostream_t* stream, bool bool_value) {
  return EncodeUnsignedVarint(
      stream, google_firestore_v1beta1_Value_boolean_value_tag, bool_value);
}

bool DecodeBool(pb_istream_t* stream) {
  uint64_t varint = DecodeUnsignedVarint(stream);
  switch (varint) {
    case 0:
      return false;
    case 1:
      return true;
    default:
      // TODO(rsgowman): figure out error handling
      abort();
  }
}

}  // namespace

using firebase::firestore::model::FieldValue;

Serializer::TypedValue Serializer::EncodeFieldValue(
    const FieldValue& field_value) {
  Serializer::TypedValue proto_value{
      field_value.type(), google_firestore_v1beta1_Value_init_default};
  switch (field_value.type()) {
    case FieldValue::Type::Null:
      proto_value.value.null_value = google_protobuf_NullValue_NULL_VALUE;
      break;
    case FieldValue::Type::Boolean:
      if (field_value == FieldValue::TrueValue()) {
        proto_value.value.boolean_value = true;
      } else {
        FIREBASE_DEV_ASSERT(field_value == FieldValue::FalseValue());
        proto_value.value.boolean_value = false;
      }
      break;
    default:
      // TODO(rsgowman): implement the other types
      abort();
  }
  return proto_value;
}

void Serializer::EncodeTypedValue(const TypedValue& value,
                                  std::vector<uint8_t>* out_bytes) {
  // TODO(rsgowman): how large should the output buffer be? Do some
  // investigation to see if we can get nanopb to tell us how much space it's
  // going to need.
  uint8_t buf[1024];
  pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf));
  switch (value.type) {
    case FieldValue::Type::Null:
      EncodeNull(&stream);
      break;

    case FieldValue::Type::Boolean:
      EncodeBool(&stream, value.value.boolean_value);
      break;

    default:
      // TODO(rsgowman): implement the other types
      abort();
  }

  out_bytes->insert(out_bytes->end(), buf, buf + stream.bytes_written);
}

FieldValue Serializer::DecodeFieldValue(
    const Serializer::TypedValue& value_proto) {
  switch (value_proto.type) {
    case FieldValue::Type::Null:
      return FieldValue::NullValue();
    case FieldValue::Type::Boolean:
      return FieldValue::BooleanValue(value_proto.value.boolean_value);
    default:
      // TODO(rsgowman): implement the other types
      abort();
  }
}

Serializer::TypedValue Serializer::DecodeTypedValue(const uint8_t* bytes,
                                                    size_t length) {
  pb_istream_t stream = pb_istream_from_buffer(bytes, length);
  pb_wire_type_t wire_type;
  uint32_t tag;
  bool eof;
  bool status = pb_decode_tag(&stream, &wire_type, &tag, &eof);
  if (!status || wire_type != PB_WT_VARINT) {
    // TODO(rsgowman): figure out error handling
    abort();
  }

  Serializer::TypedValue result{FieldValue::Type::Null,
                                google_firestore_v1beta1_Value_init_default};
  switch (tag) {
    case google_firestore_v1beta1_Value_null_value_tag:
      result.type = FieldValue::Type::Null;
      DecodeNull(&stream);
      break;
    case google_firestore_v1beta1_Value_boolean_value_tag:
      result.type = FieldValue::Type::Boolean;
      result.value.boolean_value = DecodeBool(&stream);
      break;

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

  return result;
}

bool operator==(const Serializer::TypedValue& lhs,
                const Serializer::TypedValue& rhs) {
  if (lhs.type != rhs.type) {
    return false;
  }

  switch (lhs.type) {
    case FieldValue::Type::Null:
      FIREBASE_DEV_ASSERT(lhs.value.null_value ==
                          google_protobuf_NullValue_NULL_VALUE);
      FIREBASE_DEV_ASSERT(rhs.value.null_value ==
                          google_protobuf_NullValue_NULL_VALUE);
      return true;
    case FieldValue::Type::Boolean:
      return lhs.value.boolean_value == rhs.value.boolean_value;
    default:
      // TODO(rsgowman): implement the other types
      abort();
  }
}

}  // namespace remote
}  // namespace firestore
}  // namespace firebase