Skip to content

Commit 4858fce

Browse files
var-consta-maurice
authored andcommitted
Fix almost all the remaining Unity leaks.
Make sure that C# code never directly refers to `std::vector` or `std::unordered_map`. As a side effect, this also eliminates types like `SWIGTYPE_p_std__vector` being generated (with the exception of `std::vector<FieldValue>` which comes from some of the `Query` member functions, to be fixed in a follow-up). Also make the wrappers into template classes to cut down boilerplate. There are 15 remaining leaks after running all the Unity testapp tests. They are not related to C++ standard library containers. PiperOrigin-RevId: 315339692
1 parent 1afc88e commit 4858fce

File tree

4 files changed

+334
-327
lines changed

4 files changed

+334
-327
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_INCLUDE_FIREBASE_CSHARP_MAP_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_INCLUDE_FIREBASE_CSHARP_MAP_H_
3+
4+
#include <string>
5+
#include <vector>
6+
7+
#include "app/meta/move.h"
8+
#include "firestore/src/common/futures.h"
9+
#include "firebase/firestore/document_reference.h"
10+
#include "firebase/firestore/document_snapshot.h"
11+
#include "firebase/firestore/field_path.h"
12+
#include "firebase/firestore/field_value.h"
13+
#include "firebase/firestore/query.h"
14+
#include "firebase/firestore/set_options.h"
15+
#include "firebase/firestore/transaction.h"
16+
#include "firebase/firestore/write_batch.h"
17+
18+
namespace firebase {
19+
namespace firestore {
20+
namespace csharp {
21+
22+
// Simple wrappers to avoid exposing C++ standard library containers to SWIG.
23+
//
24+
// While it's normally possible to work with standard library containers in SWIG
25+
// (by instantiating them for each template type used via the `%template`
26+
// directive), issues in the build environment make that approach too
27+
// complicated to be worth it. Instead, use simple wrappers and make sure the
28+
// underlying containers are never exposed to C#.
29+
//
30+
// Note: most of the time, these classes should be declared with a `using`
31+
// statement to ensure predictable lifetime of the object when dealing with
32+
// iterators or unsafe views.
33+
34+
// Wraps `std::unordered_map<K, V>` for use in C# code.
35+
// Note: `V` has to be default-constructible with a sensible default value.
36+
template <typename K, typename V>
37+
class Map {
38+
#ifdef STLPORT
39+
#define FIRESTORE_STD_NS std::tr1
40+
#else
41+
#define FIRESTORE_STD_NS std
42+
#endif
43+
using ContainerT = FIRESTORE_STD_NS::unordered_map<K, V>;
44+
using IterT = typename ContainerT::const_iterator;
45+
#undef FIRESTORE_STD_NS
46+
47+
public:
48+
Map() = default;
49+
50+
std::size_t Size() const { return container_.size(); }
51+
52+
// The returned reference is only valid as long as this `Map` is
53+
// valid. In C#, declare the map with a `using` statement to ensure its
54+
// lifetime exceeds the lifetime of the reference.
55+
const V& GetUnsafeView(const K& key) const {
56+
auto found = container_.find(key);
57+
if (found != container_.end()) {
58+
return found->second;
59+
}
60+
61+
return InvalidValue();
62+
}
63+
64+
V GetCopy(const K& key) const {
65+
// Note: this is safe because the reference returned by `GetUnsafeView` is
66+
// copied into the return value.
67+
return GetUnsafeView(key);
68+
}
69+
70+
void Insert(const K& key, const V& value) { container_[key] = value; }
71+
72+
class MapIterator {
73+
public:
74+
bool HasMore() const { return cur_ != end_; }
75+
76+
void Advance() { ++cur_; }
77+
78+
const K& UnsafeKeyView() const { return cur_->first; }
79+
const V& UnsafeValueView() const { return cur_->second; }
80+
81+
K KeyCopy() const { return cur_->first; }
82+
V ValueCopy() const { return cur_->second; }
83+
84+
private:
85+
friend class Map;
86+
explicit MapIterator(const Map& wrapper)
87+
: cur_{wrapper.Unwrap().begin()}, end_{wrapper.Unwrap().end()} {}
88+
89+
Map::IterT cur_, end_;
90+
};
91+
92+
MapIterator Iterator() const { return MapIterator(*this); }
93+
94+
// Note: this is a named function and not a constructor to make it easier to
95+
// ignore in SWIG.
96+
static Map Wrap(const std::unordered_map<K, V>& container) {
97+
return Map(container);
98+
}
99+
100+
const std::unordered_map<K, V>& Unwrap() const { return container_; }
101+
102+
private:
103+
explicit Map(const std::unordered_map<K, V>& container)
104+
: container_(container) {}
105+
106+
static const V& InvalidValue() {
107+
static V value;
108+
return value;
109+
}
110+
111+
ContainerT container_;
112+
};
113+
114+
inline Map<std::string, FieldValue> ConvertFieldValueToMap(
115+
const FieldValue& field_value) {
116+
return Map<std::string, FieldValue>::Wrap(field_value.map_value());
117+
}
118+
119+
inline FieldValue ConvertMapToFieldValue(
120+
const Map<std::string, FieldValue>& wrapper) {
121+
return FieldValue::Map(wrapper.Unwrap());
122+
}
123+
124+
inline FieldValue ConvertSnapshotToFieldValue(
125+
const DocumentSnapshot& snapshot,
126+
DocumentSnapshot::ServerTimestampBehavior stb) {
127+
return FieldValue::Map(snapshot.GetData(stb));
128+
}
129+
130+
inline void TransactionUpdate(Transaction* transaction,
131+
const DocumentReference& doc,
132+
const FieldValue& field_value) {
133+
transaction->Update(doc, field_value.map_value());
134+
}
135+
136+
inline void TransactionUpdate(
137+
Transaction* transaction, const DocumentReference& doc,
138+
const Map<std::string, FieldValue>& wrapper) {
139+
transaction->Update(doc, wrapper.Unwrap());
140+
}
141+
142+
inline void TransactionUpdate(
143+
Transaction* transaction, const DocumentReference& doc,
144+
const Map<FieldPath, FieldValue>& wrapper) {
145+
transaction->Update(doc, wrapper.Unwrap());
146+
}
147+
148+
inline void WriteBatchUpdate(WriteBatch* batch,
149+
const DocumentReference& doc,
150+
const FieldValue& field_value) {
151+
batch->Update(doc, field_value.map_value());
152+
}
153+
154+
inline void WriteBatchUpdate(
155+
WriteBatch* batch, const DocumentReference& doc,
156+
const Map<std::string, FieldValue>& wrapper) {
157+
batch->Update(doc, wrapper.Unwrap());
158+
}
159+
160+
inline void WriteBatchUpdate(
161+
WriteBatch* batch, const DocumentReference& doc,
162+
const Map<FieldPath, FieldValue>& wrapper) {
163+
batch->Update(doc, wrapper.Unwrap());
164+
}
165+
166+
inline Future<void> DocumentReferenceSet(DocumentReference& doc,
167+
const FieldValue& field_value,
168+
const SetOptions& options) {
169+
return doc.Set(field_value.map_value(), options);
170+
}
171+
172+
inline Future<void> DocumentReferenceUpdate(
173+
DocumentReference& doc, const FieldValue& field_value) {
174+
return doc.Update(field_value.map_value());
175+
}
176+
177+
inline Future<void> DocumentReferenceUpdate(
178+
DocumentReference& doc, const Map<FieldPath, FieldValue>& wrapper) {
179+
return doc.Update(wrapper.Unwrap());
180+
}
181+
182+
inline Query QueryWhereArrayContainsAny(Query& query,
183+
const std::string& field,
184+
const FieldValue& values) {
185+
// TODO(b/158342776): prevent an avoidable copy. SWIG would allocate a new
186+
// `Query` on the heap and initialize it with a copy of this return value.
187+
return query.WhereArrayContainsAny(field, values.array_value());
188+
}
189+
190+
inline Query QueryWhereArrayContainsAny(Query& query,
191+
const FieldPath& field,
192+
const FieldValue& values) {
193+
return query.WhereArrayContainsAny(field, values.array_value());
194+
}
195+
196+
inline Query QueryWhereIn(Query& query, const std::string& field,
197+
const FieldValue& values) {
198+
return query.WhereIn(field, values.array_value());
199+
}
200+
201+
inline Query QueryWhereIn(Query& query, const FieldPath& field,
202+
const FieldValue& values) {
203+
return query.WhereIn(field, values.array_value());
204+
}
205+
206+
inline Query QueryStartAt(Query& query, const FieldValue& values) {
207+
return query.StartAt(values.array_value());
208+
}
209+
210+
inline Query QueryStartAfter(Query& query, const FieldValue& values) {
211+
return query.StartAfter(values.array_value());
212+
}
213+
214+
inline Query QueryEndBefore(Query& query, const FieldValue& values) {
215+
return query.EndBefore(values.array_value());
216+
}
217+
218+
inline Query QueryEndAt(Query& query, const FieldValue& values) {
219+
return query.EndAt(values.array_value());
220+
}
221+
222+
inline void TransactionSet(Transaction& transaction,
223+
const DocumentReference& document,
224+
const FieldValue& data,
225+
const SetOptions& options) {
226+
transaction.Set(document, data.map_value(), options);
227+
}
228+
229+
inline void WriteBatchSet(WriteBatch& write_batch,
230+
const DocumentReference& document,
231+
const FieldValue& data,
232+
const SetOptions& options) {
233+
write_batch.Set(document, data.map_value(), options);
234+
}
235+
236+
} // namespace csharp
237+
} // namespace firestore
238+
} // namespace firebase
239+
240+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_INCLUDE_FIREBASE_CSHARP_MAP_H_

0 commit comments

Comments
 (0)