Skip to content

Commit 81b5c37

Browse files
authored
Merge pull request #8 from Microsoft/nocopy
Use a single rapidjson::Document and allocator for the entire result set
2 parents ed3f653 + 6a25a68 commit 81b5c37

File tree

10 files changed

+536
-531
lines changed

10 files changed

+536
-531
lines changed

GraphQLService.cpp

Lines changed: 94 additions & 101 deletions
Large diffs are not rendered by default.

GraphQLService.h

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace service {
2828
class schema_exception : public std::exception
2929
{
3030
public:
31-
schema_exception(const std::vector<std::string>& messages);
31+
schema_exception(std::vector<std::string>&& messages);
3232

3333
const rapidjson::Document& getErrors() const noexcept;
3434

@@ -62,13 +62,14 @@ using FragmentMap = std::unordered_map<std::string, Fragment>;
6262
// a single field.
6363
struct ResolverParams
6464
{
65+
rapidjson::Document::AllocatorType& allocator;
6566
const rapidjson::Value::ConstObject& arguments;
6667
const peg::ast_node* selection;
6768
const FragmentMap& fragments;
6869
const rapidjson::Value::ConstObject& variables;
6970
};
7071

71-
using Resolver = std::function<rapidjson::Document(ResolverParams&&)>;
72+
using Resolver = std::function<rapidjson::Value(ResolverParams&&)>;
7273
using ResolverMap = std::unordered_map<std::string, Resolver>;
7374

7475
// Binary data and opaque strings like IDs are encoded in Base64.
@@ -148,14 +149,14 @@ struct ModifiedArgument
148149
};
149150

150151
// Convert a single value to the specified type.
151-
static _Type convert(const rapidjson::Value& value);
152+
static _Type convert(rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& value);
152153

153154
// Call convert on this type without any modifiers.
154-
static _Type require(const std::string& name, const rapidjson::Value::ConstObject& arguments)
155+
static _Type require(rapidjson::Document::AllocatorType& allocator, const std::string& name, const rapidjson::Value::ConstObject& arguments)
155156
{
156157
try
157158
{
158-
return convert(arguments[name.c_str()]);
159+
return convert(allocator, arguments[name.c_str()]);
159160
}
160161
catch (const schema_exception& ex)
161162
{
@@ -167,11 +168,11 @@ struct ModifiedArgument
167168
}
168169

169170
// Wrap require in a try/catch block.
170-
static std::pair<_Type, bool> find(const std::string& name, const rapidjson::Value::ConstObject& arguments) noexcept
171+
static std::pair<_Type, bool> find(rapidjson::Document::AllocatorType& allocator, const std::string& name, const rapidjson::Value::ConstObject& arguments) noexcept
171172
{
172173
try
173174
{
174-
return { require(name, arguments), true };
175+
return { require(allocator, name, arguments), true };
175176
}
176177
catch (const schema_exception&)
177178
{
@@ -182,16 +183,16 @@ struct ModifiedArgument
182183
// Peel off the none modifier. If it's included, it should always be last in the list.
183184
template <TypeModifier _Modifier = TypeModifier::None , TypeModifier... _Other >
184185
static typename std::enable_if<TypeModifier::None == _Modifier && sizeof...(_Other) == 0, _Type>::type require(
185-
const std::string& name, const rapidjson::Value::ConstObject& arguments)
186+
rapidjson::Document::AllocatorType& allocator, const std::string& name, const rapidjson::Value::ConstObject& arguments)
186187
{
187188
// Just call through to the non-template method without the modifiers.
188-
return require(name, arguments);
189+
return require(allocator, name, arguments);
189190
}
190191

191192
// Peel off nullable modifiers.
192193
template <TypeModifier _Modifier, TypeModifier... _Other>
193194
static typename std::enable_if<TypeModifier::Nullable == _Modifier, typename ArgumentTraits<_Type, _Modifier, _Other...>::type>::type require(
194-
const std::string& name, const rapidjson::Value::ConstObject& arguments)
195+
rapidjson::Document::AllocatorType& allocator, const std::string& name, const rapidjson::Value::ConstObject& arguments)
195196
{
196197
const auto& valueItr = arguments.FindMember(name.c_str());
197198

@@ -201,42 +202,42 @@ struct ModifiedArgument
201202
return nullptr;
202203
}
203204

204-
auto result = require<_Other...>(name, arguments);
205+
auto result = require<_Other...>(allocator, name, arguments);
205206

206207
return std::unique_ptr<decltype(result)> { new decltype(result)(std::move(result)) };
207208
}
208209

209210
// Peel off list modifiers.
210211
template <TypeModifier _Modifier, TypeModifier... _Other>
211212
static typename std::enable_if<TypeModifier::List == _Modifier, typename ArgumentTraits<_Type, _Modifier, _Other...>::type>::type require(
212-
const std::string& name, const rapidjson::Value::ConstObject& arguments)
213+
rapidjson::Document::AllocatorType& allocator, const std::string& name, const rapidjson::Value::ConstObject& arguments)
213214
{
214215
const auto& values = arguments[name.c_str()].GetArray();
215216
typename ArgumentTraits<_Type, _Modifier, _Other...>::type result(values.Size());
216217

217218
std::transform(values.begin(), values.end(), result.begin(),
218-
[&name](const rapidjson::Value& element)
219+
[&allocator, &name](const rapidjson::Value& element)
219220
{
220-
rapidjson::Document single(rapidjson::Type::kObjectType);
221-
auto& allocator = single.GetAllocator();
222-
rapidjson::Value value;
221+
rapidjson::Value single(rapidjson::Type::kObjectType);
222+
rapidjson::Value entry;
223223

224-
value.CopyFrom(element, allocator);
225-
single.AddMember(rapidjson::StringRef(name.c_str()), value, allocator);
224+
entry.CopyFrom(element, allocator);
225+
single.AddMember(rapidjson::StringRef(name.c_str()), entry, allocator);
226226

227-
return require<_Other...>(name.c_str(), const_cast<const rapidjson::Document&>(single).GetObject());
227+
return require<_Other...>(allocator, name.c_str(), const_cast<const rapidjson::Value&>(single).GetObject());
228228
});
229229

230230
return result;
231231
}
232232

233233
// Wrap require with modifiers in a try/catch block.
234234
template <TypeModifier _Modifier, TypeModifier... _Other>
235-
static std::pair<typename ArgumentTraits<_Type, _Modifier, _Other...>::type, bool> find(const std::string& name, const rapidjson::Value::ConstObject& arguments) noexcept
235+
static std::pair<typename ArgumentTraits<_Type, _Modifier, _Other...>::type, bool> find(
236+
rapidjson::Document::AllocatorType& allocator, const std::string& name, const rapidjson::Value::ConstObject& arguments) noexcept
236237
{
237238
try
238239
{
239-
return { require<_Modifier, _Other...>(name, arguments), true };
240+
return { require<_Modifier, _Other...>(allocator, name, arguments), true };
240241
}
241242
catch (const schema_exception&)
242243
{
@@ -253,7 +254,7 @@ using FloatArgument = ModifiedArgument<double>;
253254
using StringArgument = ModifiedArgument<std::string>;
254255
using BooleanArgument = ModifiedArgument<bool>;
255256
using IdArgument = ModifiedArgument<std::vector<uint8_t>>;
256-
using ScalarArgument = ModifiedArgument<rapidjson::Document>;
257+
using ScalarArgument = ModifiedArgument<rapidjson::Value>;
257258

258259
// Each type should handle fragments with type conditions matching its own
259260
// name and any inheritted interfaces.
@@ -269,7 +270,7 @@ class Object : public std::enable_shared_from_this<Object>
269270
explicit Object(TypeNames&& typeNames, ResolverMap&& resolvers);
270271
virtual ~Object() = default;
271272

272-
rapidjson::Document resolve(const peg::ast_node& selection, const FragmentMap& fragments, const rapidjson::Value::ConstObject& variables) const;
273+
rapidjson::Value resolve(rapidjson::Document::AllocatorType& allocator, const peg::ast_node& selection, const FragmentMap& fragments, const rapidjson::Value::ConstObject& variables) const;
273274

274275
private:
275276
TypeNames _typeNames;
@@ -311,13 +312,13 @@ struct ModifiedResult
311312
};
312313

313314
// Convert a single value of the specified type to JSON.
314-
static rapidjson::Document convert(typename std::conditional<std::is_base_of<Object, _Type>::value, std::shared_ptr<Object>, typename ResultTraits<_Type>::type&&>::type result,
315+
static rapidjson::Value convert(typename std::conditional<std::is_base_of<Object, _Type>::value, std::shared_ptr<Object>, typename ResultTraits<_Type>::type&&>::type result,
315316
ResolverParams&& params);
316317

317318
// Peel off the none modifier. If it's included, it should always be last in the list.
318319
template <TypeModifier _Modifier = TypeModifier::None, TypeModifier... _Other>
319320
static typename std::enable_if<TypeModifier::None == _Modifier && sizeof...(_Other) == 0 && !std::is_same<Object, _Type>::value && std::is_base_of<Object, _Type>::value,
320-
rapidjson::Document>::type convert(typename ResultTraits<_Type>::type&& result, ResolverParams&& params)
321+
rapidjson::Value>::type convert(typename ResultTraits<_Type>::type&& result, ResolverParams&& params)
321322
{
322323
// Call through to the Object specialization with a static_pointer_cast for subclasses of Object.
323324
static_assert(std::is_same<std::shared_ptr<_Type>, typename ResultTraits<_Type>::type>::value, "this is the derived object type");
@@ -327,7 +328,7 @@ struct ModifiedResult
327328
// Peel off the none modifier. If it's included, it should always be last in the list.
328329
template <TypeModifier _Modifier = TypeModifier::None, TypeModifier... _Other>
329330
static typename std::enable_if<TypeModifier::None == _Modifier && sizeof...(_Other) == 0 && (std::is_same<Object, _Type>::value || !std::is_base_of<Object, _Type>::value),
330-
rapidjson::Document>::type convert(typename ResultTraits<_Type>::type&& result, ResolverParams&& params)
331+
rapidjson::Value>::type convert(typename ResultTraits<_Type>::type&& result, ResolverParams&& params)
331332
{
332333
// Just call through to the partial specialization without the modifier.
333334
return convert(std::move(result), std::move(params));
@@ -336,11 +337,11 @@ struct ModifiedResult
336337
// Peel off final nullable modifiers for std::shared_ptr of Object and subclasses of Object.
337338
template <TypeModifier _Modifier, TypeModifier... _Other>
338339
static typename std::enable_if<TypeModifier::Nullable == _Modifier && std::is_same<std::shared_ptr<_Type>, typename ResultTraits<_Type, _Other...>::type>::value,
339-
rapidjson::Document>::type convert(typename ResultTraits<_Type, _Modifier, _Other...>::type&& result, ResolverParams&& params)
340+
rapidjson::Value>::type convert(typename ResultTraits<_Type, _Modifier, _Other...>::type&& result, ResolverParams&& params)
340341
{
341342
if (!result)
342343
{
343-
return rapidjson::Document(rapidjson::Type::kNullType);
344+
return rapidjson::Value(rapidjson::Type::kNullType);
344345
}
345346

346347
return convert<_Other...>(std::move(result), std::move(params));
@@ -349,14 +350,14 @@ struct ModifiedResult
349350
// Peel off nullable modifiers for anything else, which should all be std::unique_ptr.
350351
template <TypeModifier _Modifier, TypeModifier... _Other>
351352
static typename std::enable_if<TypeModifier::Nullable == _Modifier && !std::is_same<std::shared_ptr<_Type>, typename ResultTraits<_Type, _Other...>::type>::value,
352-
rapidjson::Document>::type convert(typename ResultTraits<_Type, _Modifier, _Other...>::type&& result, ResolverParams&& params)
353+
rapidjson::Value>::type convert(typename ResultTraits<_Type, _Modifier, _Other...>::type&& result, ResolverParams&& params)
353354
{
354355
static_assert(std::is_same<std::unique_ptr<typename ResultTraits<_Type, _Other...>::type>, typename ResultTraits<_Type, _Modifier, _Other...>::type>::value,
355356
"this is the unique_ptr version");
356357

357358
if (!result)
358359
{
359-
return rapidjson::Document(rapidjson::Type::kNullType);
360+
return rapidjson::Value(rapidjson::Type::kNullType);
360361
}
361362

362363
return convert<_Other...>(std::move(*result), std::move(params));
@@ -365,19 +366,15 @@ struct ModifiedResult
365366
// Peel off list modifiers.
366367
template <TypeModifier _Modifier, TypeModifier... _Other>
367368
static typename std::enable_if<TypeModifier::List == _Modifier,
368-
rapidjson::Document>::type convert(typename ResultTraits<_Type, _Modifier, _Other...>::type&& result, ResolverParams&& params)
369+
rapidjson::Value>::type convert(typename ResultTraits<_Type, _Modifier, _Other...>::type&& result, ResolverParams&& params)
369370
{
370-
auto value = rapidjson::Document(rapidjson::Type::kArrayType);
371-
auto& allocator = value.GetAllocator();
371+
auto value = rapidjson::Value(rapidjson::Type::kArrayType);
372372

373-
value.Reserve(result.size(), allocator);
373+
value.Reserve(result.size(), params.allocator);
374374

375375
for (auto& entry : result)
376376
{
377-
rapidjson::Value element;
378-
379-
element.CopyFrom(convert<_Other...>(std::move(entry), ResolverParams(params)), allocator);
380-
value.PushBack(element, allocator);
377+
value.PushBack(convert<_Other...>(std::move(entry), ResolverParams(params)), params.allocator);
381378
}
382379

383380
return value;
@@ -392,7 +389,7 @@ using FloatResult = ModifiedResult<double>;
392389
using StringResult = ModifiedResult<std::string>;
393390
using BooleanResult = ModifiedResult<bool>;
394391
using IdResult = ModifiedResult<std::vector<unsigned char>>;
395-
using ScalarResult = ModifiedResult<rapidjson::Document>;
392+
using ScalarResult = ModifiedResult<rapidjson::Value>;
396393
using ObjectResult = ModifiedResult<Object>;
397394

398395
// Request scans the fragment definitions and finds the right operation definition to interpret
@@ -410,16 +407,16 @@ class Request : public std::enable_shared_from_this<Request>
410407
TypeMap _operations;
411408
};
412409

413-
// SelectionVisitor visits the AST and resolves a field or fragment, unless its skipped by
410+
// SelectionVisitor visits the AST and resolves a field or fragment, unless it's skipped by
414411
// a directive or type condition.
415412
class SelectionVisitor
416413
{
417414
public:
418-
SelectionVisitor(const FragmentMap& fragments, const rapidjson::Document::ConstObject& variables, const TypeNames& typeNames, const ResolverMap& resolvers);
415+
SelectionVisitor(rapidjson::Document::AllocatorType& allocator, const FragmentMap& fragments, const rapidjson::Document::ConstObject& variables, const TypeNames& typeNames, const ResolverMap& resolvers);
419416

420417
void visit(const peg::ast_node& selection);
421418

422-
rapidjson::Document getValues();
419+
rapidjson::Value getValues();
423420

424421
private:
425422
bool shouldSkip(const std::vector<std::unique_ptr<peg::ast_node>>* directives) const;
@@ -428,23 +425,25 @@ class SelectionVisitor
428425
void visitFragmentSpread(const peg::ast_node& fragmentSpread);
429426
void visitInlineFragment(const peg::ast_node& inlineFragment);
430427

428+
rapidjson::Document::AllocatorType& _allocator;
431429
const FragmentMap& _fragments;
432430
const rapidjson::Document::ConstObject& _variables;
433431
const TypeNames& _typeNames;
434432
const ResolverMap& _resolvers;
435-
rapidjson::Document _values;
433+
434+
rapidjson::Value _values;
436435
};
437436

438437
// ValueVisitor visits the AST and builds a JSON representation of any value
439438
// hardcoded or referencing a variable in an operation.
440439
class ValueVisitor
441440
{
442441
public:
443-
ValueVisitor(const rapidjson::Document::ConstObject& variables);
442+
ValueVisitor(rapidjson::Document::AllocatorType& allocator, const rapidjson::Document::ConstObject& variables);
444443

445444
void visit(const peg::ast_node& value);
446445

447-
rapidjson::Document getValue();
446+
rapidjson::Value getValue();
448447

449448
private:
450449
void visitVariable(const peg::ast_node& variable);
@@ -457,8 +456,9 @@ class ValueVisitor
457456
void visitListValue(const peg::ast_node& listValue);
458457
void visitObjectValue(const peg::ast_node& objectValue);
459458

459+
rapidjson::Document::AllocatorType& _allocator;
460460
const rapidjson::Document::ConstObject& _variables;
461-
rapidjson::Document _value;
461+
rapidjson::Value _value;
462462
};
463463

464464
// FragmentDefinitionVisitor visits the AST and collects all of the fragment

0 commit comments

Comments
 (0)