Skip to content

Commit 52890bf

Browse files
authored
Add Index encoders (#9247)
* Index encoders * Add includes * Fix lint errors * Delete TODOs * Delete a line * Feedback. * Grammar * More comment.
1 parent b1c5428 commit 52890bf

File tree

9 files changed

+454
-3
lines changed

9 files changed

+454
-3
lines changed

Firestore/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ firebase_ios_glob(
197197
src/credentials/*.h
198198
src/immutable/*.cc
199199
src/immutable/*.h
200+
src/index/*.cc
201+
src/index/*.h
200202
src/local/*.cc
201203
src/local/*.h
202204
src/model/*.cc
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "Firestore/core/src/index/firestore_index_value_writer.h"
18+
19+
#include <cmath>
20+
#include <limits>
21+
#include <string>
22+
23+
#include "Firestore/core/src/model/resource_path.h"
24+
#include "Firestore/core/src/nanopb/nanopb_util.h"
25+
26+
namespace firebase {
27+
namespace firestore {
28+
namespace index {
29+
namespace {
30+
31+
// Note: This code is copied from the backend. Code that is not used by
32+
// Firestore was removed.
33+
34+
// The client SDK only supports references to documents from the same database.
35+
// We can skip the first five segments.
36+
constexpr int DocumentNameOffset = 5;
37+
38+
enum IndexType {
39+
kNull = 5,
40+
kBoolean = 10,
41+
kNan = 13,
42+
kNumber = 15,
43+
kTimestamp = 20,
44+
kString = 25,
45+
kBlob = 30,
46+
kReference = 37,
47+
kGeopoint = 45,
48+
kArray = 50,
49+
kMap = 55,
50+
kReferenceSegment = 60,
51+
// A terminator that indicates that a truncatable value was not truncated.
52+
// This must be smaller than all other type labels.
53+
kNotTruncated = 2
54+
};
55+
56+
void WriteValueTypeLabel(DirectionalIndexByteEncoder* encoder, int type_order) {
57+
encoder->WriteLong(type_order);
58+
}
59+
60+
void WriteUnlabeledIndexString(pb_bytes_array_t* string_index,
61+
DirectionalIndexByteEncoder* encoder) {
62+
encoder->WriteString(nanopb::MakeStringView(string_index));
63+
}
64+
65+
void WriteUnlabeledIndexString(const std::string& string_index,
66+
DirectionalIndexByteEncoder* encoder) {
67+
encoder->WriteString(string_index);
68+
}
69+
70+
void WriteIndexString(pb_bytes_array_t* string_index,
71+
DirectionalIndexByteEncoder* encoder) {
72+
WriteValueTypeLabel(encoder, IndexType::kString);
73+
WriteUnlabeledIndexString(string_index, encoder);
74+
}
75+
76+
void WriteTruncationMarker(DirectionalIndexByteEncoder* encoder) {
77+
// While the SDK does not implement truncation, the truncation marker is used
78+
// to terminate all variable length values (which are strings, bytes,
79+
// references, arrays and maps).
80+
encoder->WriteLong(IndexType::kNotTruncated);
81+
}
82+
83+
void WriteIndexEntityRef(pb_bytes_array_t* reference_value,
84+
DirectionalIndexByteEncoder* encoder) {
85+
WriteValueTypeLabel(encoder, IndexType::kReference);
86+
87+
auto path = model::ResourcePath::FromStringView(
88+
nanopb::MakeStringView(reference_value));
89+
auto num_segments = path.size();
90+
for (size_t index = DocumentNameOffset; index < num_segments; ++index) {
91+
const std::string& segment = path[index];
92+
WriteValueTypeLabel(encoder, IndexType::kReferenceSegment);
93+
WriteUnlabeledIndexString(segment, encoder);
94+
}
95+
}
96+
97+
void WriteIndexValueAux(const google_firestore_v1_Value& index_value,
98+
DirectionalIndexByteEncoder* encoder);
99+
100+
void WriteIndexArray(const google_firestore_v1_ArrayValue& array_index_value,
101+
DirectionalIndexByteEncoder* encoder) {
102+
WriteValueTypeLabel(encoder, IndexType::kArray);
103+
for (pb_size_t i = 0; i < array_index_value.values_count; ++i) {
104+
WriteIndexValueAux(array_index_value.values[i], encoder);
105+
}
106+
}
107+
108+
void WriteIndexMap(google_firestore_v1_MapValue map_index_value,
109+
DirectionalIndexByteEncoder* encoder) {
110+
WriteValueTypeLabel(encoder, IndexType::kMap);
111+
for (pb_size_t i = 0; i < map_index_value.fields_count; ++i) {
112+
WriteIndexString(map_index_value.fields[i].key, encoder);
113+
WriteIndexValueAux(map_index_value.fields[i].value, encoder);
114+
}
115+
}
116+
117+
void WriteIndexValueAux(const google_firestore_v1_Value& index_value,
118+
DirectionalIndexByteEncoder* encoder) {
119+
switch (index_value.which_value_type) {
120+
case google_firestore_v1_Value_null_value_tag: {
121+
WriteValueTypeLabel(encoder, IndexType::kNull);
122+
break;
123+
}
124+
case google_firestore_v1_Value_boolean_value_tag: {
125+
WriteValueTypeLabel(encoder, IndexType::kBoolean);
126+
encoder->WriteLong(index_value.boolean_value ? 1 : 0);
127+
break;
128+
}
129+
case google_firestore_v1_Value_double_value_tag: {
130+
double number = index_value.double_value;
131+
if (std::isnan(number)) {
132+
WriteValueTypeLabel(encoder, IndexType::kNan);
133+
break;
134+
}
135+
WriteValueTypeLabel(encoder, IndexType::kNumber);
136+
if (number == -0.0) {
137+
// -0.0, 0 and 0.0 are all considered the same
138+
encoder->WriteDouble(0.0);
139+
} else {
140+
encoder->WriteDouble(number);
141+
}
142+
break;
143+
}
144+
case google_firestore_v1_Value_integer_value_tag: {
145+
WriteValueTypeLabel(encoder, IndexType::kNumber);
146+
// Write as double instead of integer so 0 and 0.0 are considered the
147+
// same.
148+
encoder->WriteDouble(index_value.integer_value);
149+
break;
150+
}
151+
case google_firestore_v1_Value_timestamp_value_tag: {
152+
const auto& timestamp = index_value.timestamp_value;
153+
WriteValueTypeLabel(encoder, IndexType::kTimestamp);
154+
encoder->WriteLong(timestamp.seconds);
155+
encoder->WriteLong(timestamp.nanos);
156+
break;
157+
}
158+
case google_firestore_v1_Value_string_value_tag: {
159+
WriteIndexString(index_value.string_value, encoder);
160+
WriteTruncationMarker(encoder);
161+
break;
162+
}
163+
case google_firestore_v1_Value_bytes_value_tag: {
164+
WriteValueTypeLabel(encoder, IndexType::kBlob);
165+
encoder->WriteBytes(index_value.bytes_value);
166+
WriteTruncationMarker(encoder);
167+
break;
168+
}
169+
case google_firestore_v1_Value_reference_value_tag: {
170+
WriteIndexEntityRef(index_value.reference_value, encoder);
171+
break;
172+
}
173+
case google_firestore_v1_Value_geo_point_value_tag: {
174+
const auto& geo_point = index_value.geo_point_value;
175+
WriteValueTypeLabel(encoder, IndexType::kGeopoint);
176+
encoder->WriteDouble(geo_point.latitude);
177+
encoder->WriteDouble(geo_point.longitude);
178+
break;
179+
}
180+
case google_firestore_v1_Value_map_value_tag:
181+
// model::MaxValue() is sentinel map value (see the comment there).
182+
// In that case, we encode the max int value instead.
183+
if (model::IsMaxValue(index_value)) {
184+
WriteValueTypeLabel(encoder, std::numeric_limits<int>::max());
185+
break;
186+
}
187+
WriteIndexMap(index_value.map_value, encoder);
188+
WriteTruncationMarker(encoder);
189+
break;
190+
case google_firestore_v1_Value_array_value_tag: {
191+
WriteIndexArray(index_value.array_value, encoder);
192+
WriteTruncationMarker(encoder);
193+
break;
194+
}
195+
default:
196+
HARD_FAIL("Unknown index value type");
197+
}
198+
}
199+
200+
} // namespace
201+
202+
/** Writes an index value. */
203+
void WriteIndexValue(const google_firestore_v1_Value& value,
204+
DirectionalIndexByteEncoder* encoder) {
205+
WriteIndexValueAux(value, encoder);
206+
// Write separator to split index values (see
207+
// go/firestore-storage-format#encodings).
208+
encoder->WriteInfinity();
209+
}
210+
211+
} // namespace index
212+
} // namespace firestore
213+
} // namespace firebase
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef FIRESTORE_CORE_SRC_INDEX_FIRESTORE_INDEX_VALUE_WRITER_H_
18+
#define FIRESTORE_CORE_SRC_INDEX_FIRESTORE_INDEX_VALUE_WRITER_H_
19+
20+
#include "Firestore/core/src/index/index_byte_encoder.h"
21+
#include "Firestore/core/src/nanopb/nanopb_util.h"
22+
23+
namespace firebase {
24+
namespace firestore {
25+
namespace index {
26+
27+
/**
28+
* Writes an index value using the given encoder. The encoder writes the encoded
29+
* bytes into a buffer maintained by `IndexEncodingBuffer` who owns the
30+
* `encoder`.
31+
*/
32+
void WriteIndexValue(const google_firestore_v1_Value& value,
33+
DirectionalIndexByteEncoder* encoder);
34+
35+
} // namespace index
36+
} // namespace firestore
37+
} // namespace firebase
38+
39+
#endif // FIRESTORE_CORE_SRC_INDEX_FIRESTORE_INDEX_VALUE_WRITER_H_

0 commit comments

Comments
 (0)