Use Allocator for vector in TypedValue
Reviewers: mtomic, llugovic, mferencevic Reviewed By: mtomic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2086
This commit is contained in:
parent
0152ac2dfe
commit
bb2dbc290f
@ -822,24 +822,25 @@ class ExpandVariableCursor : public Cursor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for appending an edge to the list on the frame.
|
// Helper function for appending an edge to the list on the frame.
|
||||||
void AppendEdge(const EdgeAccessor &new_edge,
|
void AppendEdge(
|
||||||
std::vector<TypedValue> &edges_on_frame) {
|
const EdgeAccessor &new_edge,
|
||||||
|
std::vector<TypedValue, utils::Allocator<TypedValue>> *edges_on_frame) {
|
||||||
// We are placing an edge on the frame. It is possible that there already
|
// We are placing an edge on the frame. It is possible that there already
|
||||||
// exists an edge on the frame for this level. If so first remove it.
|
// exists an edge on the frame for this level. If so first remove it.
|
||||||
DCHECK(edges_.size() > 0) << "Edges are empty";
|
DCHECK(edges_.size() > 0) << "Edges are empty";
|
||||||
if (self_.is_reverse_) {
|
if (self_.is_reverse_) {
|
||||||
// TODO: This is innefficient, we should look into replacing
|
// TODO: This is innefficient, we should look into replacing
|
||||||
// vector with something else for TypedValue::List.
|
// vector with something else for TypedValue::List.
|
||||||
size_t diff = edges_on_frame.size() -
|
size_t diff = edges_on_frame->size() -
|
||||||
std::min(edges_on_frame.size(), edges_.size() - 1U);
|
std::min(edges_on_frame->size(), edges_.size() - 1U);
|
||||||
if (diff > 0U)
|
if (diff > 0U)
|
||||||
edges_on_frame.erase(edges_on_frame.begin(),
|
edges_on_frame->erase(edges_on_frame->begin(),
|
||||||
edges_on_frame.begin() + diff);
|
edges_on_frame->begin() + diff);
|
||||||
edges_on_frame.insert(edges_on_frame.begin(), new_edge);
|
edges_on_frame->insert(edges_on_frame->begin(), new_edge);
|
||||||
} else {
|
} else {
|
||||||
edges_on_frame.resize(
|
edges_on_frame->resize(
|
||||||
std::min(edges_on_frame.size(), edges_.size() - 1U));
|
std::min(edges_on_frame->size(), edges_.size() - 1U));
|
||||||
edges_on_frame.emplace_back(new_edge);
|
edges_on_frame->emplace_back(new_edge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,8 +873,7 @@ class ExpandVariableCursor : public Cursor {
|
|||||||
if (edges_.empty()) return false;
|
if (edges_.empty()) return false;
|
||||||
|
|
||||||
// we use this a lot
|
// we use this a lot
|
||||||
std::vector<TypedValue> &edges_on_frame =
|
auto &edges_on_frame = frame[self_.common_.edge_symbol].ValueList();
|
||||||
frame[self_.common_.edge_symbol].ValueList();
|
|
||||||
|
|
||||||
// it is possible that edges_on_frame does not contain as many
|
// it is possible that edges_on_frame does not contain as many
|
||||||
// elements as edges_ due to edge-uniqueness (when a whole layer
|
// elements as edges_ due to edge-uniqueness (when a whole layer
|
||||||
@ -903,7 +903,7 @@ class ExpandVariableCursor : public Cursor {
|
|||||||
});
|
});
|
||||||
if (found_existing) continue;
|
if (found_existing) continue;
|
||||||
|
|
||||||
AppendEdge(current_edge.first, edges_on_frame);
|
AppendEdge(current_edge.first, &edges_on_frame);
|
||||||
VertexAccessor current_vertex =
|
VertexAccessor current_vertex =
|
||||||
current_edge.second == EdgeAtom::Direction::IN
|
current_edge.second == EdgeAtom::Direction::IN
|
||||||
? current_edge.first.from()
|
? current_edge.first.from()
|
||||||
@ -1670,7 +1670,7 @@ class ConstructNamedPathCursor : public Cursor {
|
|||||||
last_was_edge_list = true;
|
last_was_edge_list = true;
|
||||||
// We need to expand all edges in the list and intermediary
|
// We need to expand all edges in the list and intermediary
|
||||||
// vertices.
|
// vertices.
|
||||||
const std::vector<TypedValue> &edges = expansion.ValueList();
|
const auto &edges = expansion.ValueList();
|
||||||
for (const auto &edge_value : edges) {
|
for (const auto &edge_value : edges) {
|
||||||
const EdgeAccessor &edge = edge_value.ValueEdge();
|
const EdgeAccessor &edge = edge_value.ValueEdge();
|
||||||
const VertexAccessor from = edge.from();
|
const VertexAccessor from = edge.from();
|
||||||
|
@ -43,8 +43,10 @@ TypedValue::TypedValue(const PropertyValue &value,
|
|||||||
return;
|
return;
|
||||||
case PropertyValue::Type::List: {
|
case PropertyValue::Type::List: {
|
||||||
type_ = Type::List;
|
type_ = Type::List;
|
||||||
auto vec = value.Value<std::vector<PropertyValue>>();
|
const auto &vec = value.Value<std::vector<PropertyValue>>();
|
||||||
new (&list_v) std::vector<TypedValue>(vec.begin(), vec.end());
|
new (&list_v) TVector(memory_);
|
||||||
|
list_v.reserve(vec.size());
|
||||||
|
list_v.assign(vec.begin(), vec.end());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case PropertyValue::Type::Map: {
|
case PropertyValue::Type::Map: {
|
||||||
@ -86,9 +88,10 @@ TypedValue::TypedValue(PropertyValue &&other, utils::MemoryResource *memory)
|
|||||||
case PropertyValue::Type::List: {
|
case PropertyValue::Type::List: {
|
||||||
type_ = Type::List;
|
type_ = Type::List;
|
||||||
auto &vec = other.Value<std::vector<PropertyValue>>();
|
auto &vec = other.Value<std::vector<PropertyValue>>();
|
||||||
new (&list_v)
|
new (&list_v) TVector(memory_);
|
||||||
std::vector<TypedValue>(std::make_move_iterator(vec.begin()),
|
list_v.reserve(vec.size());
|
||||||
std::make_move_iterator(vec.end()));
|
list_v.assign(std::make_move_iterator(vec.begin()),
|
||||||
|
std::make_move_iterator(vec.end()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PropertyValue::Type::Map: {
|
case PropertyValue::Type::Map: {
|
||||||
@ -127,7 +130,7 @@ TypedValue::TypedValue(const TypedValue &other, utils::MemoryResource *memory)
|
|||||||
new (&string_v) std::string(other.string_v);
|
new (&string_v) std::string(other.string_v);
|
||||||
return;
|
return;
|
||||||
case Type::List:
|
case Type::List:
|
||||||
new (&list_v) std::vector<TypedValue>(other.list_v);
|
new (&list_v) TVector(other.list_v, memory_);
|
||||||
return;
|
return;
|
||||||
case Type::Map:
|
case Type::Map:
|
||||||
new (&map_v) std::map<std::string, TypedValue>(other.map_v);
|
new (&map_v) std::map<std::string, TypedValue>(other.map_v);
|
||||||
@ -166,7 +169,7 @@ TypedValue::TypedValue(TypedValue &&other, utils::MemoryResource *memory)
|
|||||||
new (&string_v) std::string(std::move(other.string_v));
|
new (&string_v) std::string(std::move(other.string_v));
|
||||||
break;
|
break;
|
||||||
case Type::List:
|
case Type::List:
|
||||||
new (&list_v) std::vector<TypedValue>(std::move(other.list_v));
|
new (&list_v) TVector(std::move(other.list_v), memory_);
|
||||||
break;
|
break;
|
||||||
case Type::Map:
|
case Type::Map:
|
||||||
new (&map_v) std::map<std::string, TypedValue>(std::move(other.map_v));
|
new (&map_v) std::map<std::string, TypedValue>(std::move(other.map_v));
|
||||||
@ -181,7 +184,6 @@ TypedValue::TypedValue(TypedValue &&other, utils::MemoryResource *memory)
|
|||||||
new (&path_v) Path(std::move(other.path_v));
|
new (&path_v) Path(std::move(other.path_v));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
other = TypedValue::Null;
|
other = TypedValue::Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +243,7 @@ DEFINE_VALUE_AND_TYPE_GETTERS(bool, Bool, bool_v)
|
|||||||
DEFINE_VALUE_AND_TYPE_GETTERS(int64_t, Int, int_v)
|
DEFINE_VALUE_AND_TYPE_GETTERS(int64_t, Int, int_v)
|
||||||
DEFINE_VALUE_AND_TYPE_GETTERS(double, Double, double_v)
|
DEFINE_VALUE_AND_TYPE_GETTERS(double, Double, double_v)
|
||||||
DEFINE_VALUE_AND_TYPE_GETTERS(std::string, String, string_v)
|
DEFINE_VALUE_AND_TYPE_GETTERS(std::string, String, string_v)
|
||||||
DEFINE_VALUE_AND_TYPE_GETTERS(std::vector<TypedValue>, List, list_v)
|
DEFINE_VALUE_AND_TYPE_GETTERS(TypedValue::TVector, List, list_v)
|
||||||
DEFINE_VALUE_AND_TYPE_GETTERS(TypedValue::value_map_t, Map, map_v)
|
DEFINE_VALUE_AND_TYPE_GETTERS(TypedValue::value_map_t, Map, map_v)
|
||||||
DEFINE_VALUE_AND_TYPE_GETTERS(VertexAccessor, Vertex, vertex_v)
|
DEFINE_VALUE_AND_TYPE_GETTERS(VertexAccessor, Vertex, vertex_v)
|
||||||
DEFINE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge, edge_v)
|
DEFINE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge, edge_v)
|
||||||
@ -345,8 +347,18 @@ DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(bool, Bool, bool_v)
|
|||||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(int64_t, Int, int_v)
|
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(int64_t, Int, int_v)
|
||||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(double, Double, double_v)
|
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(double, Double, double_v)
|
||||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const std::string &, String, string_v)
|
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const std::string &, String, string_v)
|
||||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const std::vector<TypedValue> &, List,
|
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const TypedValue::TVector &, List, list_v)
|
||||||
list_v)
|
|
||||||
|
TypedValue &TypedValue::operator=(const std::vector<TypedValue> &other) {
|
||||||
|
if (type_ == Type::List) {
|
||||||
|
list_v.reserve(other.size());
|
||||||
|
list_v.assign(other.begin(), other.end());
|
||||||
|
} else {
|
||||||
|
*this = TypedValue(other, memory_);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const TypedValue::value_map_t &, Map, map_v)
|
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const TypedValue::value_map_t &, Map, map_v)
|
||||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const VertexAccessor &, Vertex, vertex_v)
|
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const VertexAccessor &, Vertex, vertex_v)
|
||||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const EdgeAccessor &, Edge, edge_v)
|
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const EdgeAccessor &, Edge, edge_v)
|
||||||
@ -356,7 +368,7 @@ DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Path &, Path, path_v)
|
|||||||
|
|
||||||
#define DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(type_param, typed_value_type, \
|
#define DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(type_param, typed_value_type, \
|
||||||
member) \
|
member) \
|
||||||
TypedValue &TypedValue::operator=(type_param &&other) noexcept { \
|
TypedValue &TypedValue::operator=(type_param &&other) { \
|
||||||
if (this->type_ == TypedValue::Type::typed_value_type) { \
|
if (this->type_ == TypedValue::Type::typed_value_type) { \
|
||||||
this->member = std::move(other); \
|
this->member = std::move(other); \
|
||||||
} else { \
|
} else { \
|
||||||
@ -367,7 +379,18 @@ DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Path &, Path, path_v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(std::string, String, string_v)
|
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(std::string, String, string_v)
|
||||||
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(std::vector<TypedValue>, List, list_v)
|
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(TypedValue::TVector, List, list_v)
|
||||||
|
|
||||||
|
TypedValue &TypedValue::operator=(std::vector<TypedValue> &&other) {
|
||||||
|
if (type_ == Type::List) {
|
||||||
|
list_v.assign(std::make_move_iterator(other.begin()),
|
||||||
|
std::make_move_iterator(other.end()));
|
||||||
|
} else {
|
||||||
|
*this = TypedValue(std::move(other), memory_);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(TypedValue::value_map_t, Map, map_v)
|
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(TypedValue::value_map_t, Map, map_v)
|
||||||
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(VertexAccessor, Vertex, vertex_v)
|
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(VertexAccessor, Vertex, vertex_v)
|
||||||
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(EdgeAccessor, Edge, edge_v)
|
DEFINE_TYPED_VALUE_MOVE_ASSIGNMENT(EdgeAccessor, Edge, edge_v)
|
||||||
@ -387,7 +410,6 @@ TypedValue &TypedValue::operator=(const TypedValue &other) {
|
|||||||
"Allocator propagation not implemented");
|
"Allocator propagation not implemented");
|
||||||
DestroyValue();
|
DestroyValue();
|
||||||
type_ = other.type_;
|
type_ = other.type_;
|
||||||
|
|
||||||
switch (other.type_) {
|
switch (other.type_) {
|
||||||
case TypedValue::Type::Null:
|
case TypedValue::Type::Null:
|
||||||
return *this;
|
return *this;
|
||||||
@ -404,7 +426,7 @@ TypedValue &TypedValue::operator=(const TypedValue &other) {
|
|||||||
new (&string_v) std::string(other.string_v);
|
new (&string_v) std::string(other.string_v);
|
||||||
return *this;
|
return *this;
|
||||||
case TypedValue::Type::List:
|
case TypedValue::Type::List:
|
||||||
new (&list_v) std::vector<TypedValue>(other.list_v);
|
new (&list_v) TVector(other.list_v, memory_);
|
||||||
return *this;
|
return *this;
|
||||||
case TypedValue::Type::Map:
|
case TypedValue::Type::Map:
|
||||||
new (&map_v) std::map<std::string, TypedValue>(other.map_v);
|
new (&map_v) std::map<std::string, TypedValue>(other.map_v);
|
||||||
@ -424,7 +446,7 @@ TypedValue &TypedValue::operator=(const TypedValue &other) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedValue &TypedValue::operator=(TypedValue &&other) noexcept {
|
TypedValue &TypedValue::operator=(TypedValue &&other) noexcept(false) {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
DestroyValue();
|
DestroyValue();
|
||||||
// NOTE: STL uses
|
// NOTE: STL uses
|
||||||
@ -436,7 +458,6 @@ TypedValue &TypedValue::operator=(TypedValue &&other) noexcept {
|
|||||||
propagate_on_container_move_assignment::value,
|
propagate_on_container_move_assignment::value,
|
||||||
"Allocator propagation not implemented");
|
"Allocator propagation not implemented");
|
||||||
type_ = other.type_;
|
type_ = other.type_;
|
||||||
|
|
||||||
switch (other.type_) {
|
switch (other.type_) {
|
||||||
case TypedValue::Type::Null:
|
case TypedValue::Type::Null:
|
||||||
break;
|
break;
|
||||||
@ -453,7 +474,7 @@ TypedValue &TypedValue::operator=(TypedValue &&other) noexcept {
|
|||||||
new (&string_v) std::string(std::move(other.string_v));
|
new (&string_v) std::string(std::move(other.string_v));
|
||||||
break;
|
break;
|
||||||
case TypedValue::Type::List:
|
case TypedValue::Type::List:
|
||||||
new (&list_v) std::vector<TypedValue>(std::move(other.list_v));
|
new (&list_v) TVector(std::move(other.list_v), memory_);
|
||||||
break;
|
break;
|
||||||
case TypedValue::Type::Map:
|
case TypedValue::Type::Map:
|
||||||
new (&map_v) std::map<std::string, TypedValue>(std::move(other.map_v));
|
new (&map_v) std::map<std::string, TypedValue>(std::move(other.map_v));
|
||||||
@ -468,10 +489,8 @@ TypedValue &TypedValue::operator=(TypedValue &&other) noexcept {
|
|||||||
new (&path_v) Path(std::move(other.path_v));
|
new (&path_v) Path(std::move(other.path_v));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
other = TypedValue::Null;
|
other = TypedValue::Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,8 +514,7 @@ void TypedValue::DestroyValue() {
|
|||||||
string_v.~string();
|
string_v.~string();
|
||||||
break;
|
break;
|
||||||
case Type::List:
|
case Type::List:
|
||||||
using namespace std;
|
list_v.~TVector();
|
||||||
list_v.~vector<TypedValue>();
|
|
||||||
break;
|
break;
|
||||||
case Type::Map:
|
case Type::Map:
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -717,7 +735,7 @@ TypedValue operator+(const TypedValue &a, const TypedValue &b) {
|
|||||||
if (a.IsNull() || b.IsNull()) return TypedValue(a.GetMemoryResource());
|
if (a.IsNull() || b.IsNull()) return TypedValue(a.GetMemoryResource());
|
||||||
|
|
||||||
if (a.IsList() || b.IsList()) {
|
if (a.IsList() || b.IsList()) {
|
||||||
std::vector<TypedValue> list;
|
TypedValue::TVector list(a.GetMemoryResource());
|
||||||
auto append_list = [&list](const TypedValue &v) {
|
auto append_list = [&list](const TypedValue &v) {
|
||||||
if (v.IsList()) {
|
if (v.IsList()) {
|
||||||
auto list2 = v.ValueList();
|
auto list2 = v.ValueList();
|
||||||
@ -728,7 +746,7 @@ TypedValue operator+(const TypedValue &a, const TypedValue &b) {
|
|||||||
};
|
};
|
||||||
append_list(a);
|
append_list(a);
|
||||||
append_list(b);
|
append_list(b);
|
||||||
return TypedValue(list, a.GetMemoryResource());
|
return TypedValue(std::move(list), a.GetMemoryResource());
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureArithmeticallyOk(a, b, true, "addition");
|
EnsureArithmeticallyOk(a, b, true, "addition");
|
||||||
@ -878,7 +896,7 @@ size_t TypedValue::Hash::operator()(const TypedValue &value) const {
|
|||||||
case TypedValue::Type::String:
|
case TypedValue::Type::String:
|
||||||
return std::hash<std::string>{}(value.Value<std::string>());
|
return std::hash<std::string>{}(value.Value<std::string>());
|
||||||
case TypedValue::Type::List: {
|
case TypedValue::Type::List: {
|
||||||
return utils::FnvCollection<std::vector<TypedValue>, TypedValue, Hash>{}(
|
return utils::FnvCollection<TypedValue::TVector, TypedValue, Hash>{}(
|
||||||
value.ValueList());
|
value.ValueList());
|
||||||
}
|
}
|
||||||
case TypedValue::Type::Map: {
|
case TypedValue::Type::Map: {
|
||||||
|
@ -20,11 +20,15 @@ namespace query {
|
|||||||
|
|
||||||
// TODO: Neo4j does overflow checking. Should we also implement it?
|
// TODO: Neo4j does overflow checking. Should we also implement it?
|
||||||
/**
|
/**
|
||||||
* Encapsulation of a value and it's type encapsulated in a class that has no
|
* Stores a query runtime value and its type.
|
||||||
* compiled-time info about that type.
|
|
||||||
*
|
*
|
||||||
* Values can be of a number of predefined types that are enumerated in
|
* Values can be of a number of predefined types that are enumerated in
|
||||||
* TypedValue::Type. Each such type corresponds to exactly one C++ type.
|
* TypedValue::Type. Each such type corresponds to exactly one C++ type.
|
||||||
|
*
|
||||||
|
* Non-primitive value types perform additional memory allocations. To tune the
|
||||||
|
* allocation scheme, each TypedValue stores a MemoryResource for said
|
||||||
|
* allocations. When copying and moving TypedValue instances, take care that the
|
||||||
|
* appropriate MemoryResource is used.
|
||||||
*/
|
*/
|
||||||
class TypedValue {
|
class TypedValue {
|
||||||
public:
|
public:
|
||||||
@ -70,6 +74,7 @@ class TypedValue {
|
|||||||
*/
|
*/
|
||||||
using unordered_set = std::unordered_set<TypedValue, Hash, BoolEqual>;
|
using unordered_set = std::unordered_set<TypedValue, Hash, BoolEqual>;
|
||||||
using value_map_t = std::map<std::string, TypedValue>;
|
using value_map_t = std::map<std::string, TypedValue>;
|
||||||
|
using TVector = std::vector<TypedValue, utils::Allocator<TypedValue>>;
|
||||||
|
|
||||||
/** Allocator type so that STL containers are aware that we need one */
|
/** Allocator type so that STL containers are aware that we need one */
|
||||||
using allocator_type = utils::Allocator<TypedValue>;
|
using allocator_type = utils::Allocator<TypedValue>;
|
||||||
@ -154,10 +159,33 @@ class TypedValue {
|
|||||||
new (&string_v) std::string(value);
|
new (&string_v) std::string(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Construct a copy using the given utils::MemoryResource */
|
||||||
TypedValue(const std::vector<TypedValue> &value,
|
TypedValue(const std::vector<TypedValue> &value,
|
||||||
utils::MemoryResource *memory = utils::NewDeleteResource())
|
utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||||
: memory_(memory), type_(Type::List) {
|
: memory_(memory), type_(Type::List) {
|
||||||
new (&list_v) std::vector<TypedValue>(value);
|
new (&list_v) TVector(memory_);
|
||||||
|
list_v.reserve(value.size());
|
||||||
|
list_v.assign(value.begin(), value.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a copy of other.
|
||||||
|
* utils::MemoryResource is obtained by calling
|
||||||
|
* std::allocator_traits<>::
|
||||||
|
* select_on_container_copy_construction(other.get_allocator()).
|
||||||
|
* Since we use utils::Allocator, which does not propagate, this means that
|
||||||
|
* memory_ will be the default utils::NewDeleteResource().
|
||||||
|
*/
|
||||||
|
TypedValue(const TVector &other)
|
||||||
|
: TypedValue(other, std::allocator_traits<utils::Allocator<TypedValue>>::
|
||||||
|
select_on_container_copy_construction(
|
||||||
|
other.get_allocator())
|
||||||
|
.GetMemoryResource()) {}
|
||||||
|
|
||||||
|
/** Construct a copy using the given utils::MemoryResource */
|
||||||
|
TypedValue(const TVector &value, utils::MemoryResource *memory)
|
||||||
|
: memory_(memory), type_(Type::List) {
|
||||||
|
new (&list_v) TVector(value, memory_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedValue(const std::map<std::string, TypedValue> &value,
|
TypedValue(const std::map<std::string, TypedValue> &value,
|
||||||
@ -200,14 +228,48 @@ class TypedValue {
|
|||||||
new (&string_v) std::string(std::move(value));
|
new (&string_v) std::string(std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedValue(std::vector<TypedValue> &&value) noexcept : type_(Type::List) {
|
/**
|
||||||
new (&list_v) std::vector<TypedValue>(std::move(value));
|
* Perform an element-wise move using default utils::NewDeleteResource().
|
||||||
|
* Other will be not be empty, though elements may be Null.
|
||||||
|
*/
|
||||||
|
TypedValue(std::vector<TypedValue> &&other)
|
||||||
|
: TypedValue(std::move(other), utils::NewDeleteResource()) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform an element-wise move of the other and use the given MemoryResource.
|
||||||
|
* Other will be not be empty, though elements may be Null.
|
||||||
|
*/
|
||||||
|
TypedValue(std::vector<TypedValue> &&other, utils::MemoryResource *memory)
|
||||||
|
: memory_(memory), type_(Type::List) {
|
||||||
|
new (&list_v) TVector(memory_);
|
||||||
|
list_v.reserve(other.size());
|
||||||
|
// std::vector<TypedValue> has std::allocator and there's no move
|
||||||
|
// constructor for std::vector using different allocator types. Since
|
||||||
|
// std::allocator is not propagated to elements, it is possible that some
|
||||||
|
// TypedValue elements have a MemoryResource that is the same as the one we
|
||||||
|
// are given. In such a case we would like to move those TypedValue
|
||||||
|
// instances, so we use move_iterator.
|
||||||
|
list_v.assign(std::make_move_iterator(other.begin()),
|
||||||
|
std::make_move_iterator(other.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedValue(std::vector<TypedValue> &&value,
|
/**
|
||||||
utils::MemoryResource *memory) noexcept
|
* Construct with the value of other.
|
||||||
|
* utils::MemoryResource is obtained from other. After the move, other will be
|
||||||
|
* left empty.
|
||||||
|
*/
|
||||||
|
TypedValue(TVector &&other) noexcept
|
||||||
|
: TypedValue(std::move(other),
|
||||||
|
other.get_allocator().GetMemoryResource()) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct with the value of other and use the given MemoryResource.
|
||||||
|
* If `other.get_allocator() != *memory`, this call will perform an
|
||||||
|
* element-wise move and other is not guaranteed to be empty.
|
||||||
|
*/
|
||||||
|
TypedValue(TVector &&other, utils::MemoryResource *memory)
|
||||||
: memory_(memory), type_(Type::List) {
|
: memory_(memory), type_(Type::List) {
|
||||||
new (&list_v) std::vector<TypedValue>(std::move(value));
|
new (&list_v) TVector(std::move(other), memory_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedValue(std::map<std::string, TypedValue> &&value) noexcept
|
TypedValue(std::map<std::string, TypedValue> &&value) noexcept
|
||||||
@ -269,6 +331,7 @@ class TypedValue {
|
|||||||
TypedValue &operator=(int64_t);
|
TypedValue &operator=(int64_t);
|
||||||
TypedValue &operator=(double);
|
TypedValue &operator=(double);
|
||||||
TypedValue &operator=(const std::string &);
|
TypedValue &operator=(const std::string &);
|
||||||
|
TypedValue &operator=(const TVector &);
|
||||||
TypedValue &operator=(const std::vector<TypedValue> &);
|
TypedValue &operator=(const std::vector<TypedValue> &);
|
||||||
TypedValue &operator=(const TypedValue::value_map_t &);
|
TypedValue &operator=(const TypedValue::value_map_t &);
|
||||||
TypedValue &operator=(const VertexAccessor &);
|
TypedValue &operator=(const VertexAccessor &);
|
||||||
@ -279,15 +342,16 @@ class TypedValue {
|
|||||||
TypedValue &operator=(const TypedValue &other);
|
TypedValue &operator=(const TypedValue &other);
|
||||||
|
|
||||||
// move assignment operators
|
// move assignment operators
|
||||||
TypedValue &operator=(std::string &&) noexcept;
|
TypedValue &operator=(std::string &&);
|
||||||
TypedValue &operator=(std::vector<TypedValue> &&) noexcept;
|
TypedValue &operator=(TVector &&);
|
||||||
TypedValue &operator=(TypedValue::value_map_t &&) noexcept;
|
TypedValue &operator=(std::vector<TypedValue> &&);
|
||||||
TypedValue &operator=(VertexAccessor &&) noexcept;
|
TypedValue &operator=(TypedValue::value_map_t &&);
|
||||||
TypedValue &operator=(EdgeAccessor &&) noexcept;
|
TypedValue &operator=(VertexAccessor &&);
|
||||||
TypedValue &operator=(Path &&) noexcept;
|
TypedValue &operator=(EdgeAccessor &&);
|
||||||
|
TypedValue &operator=(Path &&);
|
||||||
|
|
||||||
/** Move assign other, utils::MemoryResource of `this` is used. */
|
/** Move assign other, utils::MemoryResource of `this` is used. */
|
||||||
TypedValue &operator=(TypedValue &&other) noexcept;
|
TypedValue &operator=(TypedValue &&other) noexcept(false);
|
||||||
|
|
||||||
~TypedValue();
|
~TypedValue();
|
||||||
|
|
||||||
@ -320,7 +384,18 @@ class TypedValue {
|
|||||||
DECLARE_VALUE_AND_TYPE_GETTERS(int64_t, Int)
|
DECLARE_VALUE_AND_TYPE_GETTERS(int64_t, Int)
|
||||||
DECLARE_VALUE_AND_TYPE_GETTERS(double, Double)
|
DECLARE_VALUE_AND_TYPE_GETTERS(double, Double)
|
||||||
DECLARE_VALUE_AND_TYPE_GETTERS(std::string, String)
|
DECLARE_VALUE_AND_TYPE_GETTERS(std::string, String)
|
||||||
DECLARE_VALUE_AND_TYPE_GETTERS(std::vector<TypedValue>, List)
|
|
||||||
|
/**
|
||||||
|
* Get the list value.
|
||||||
|
* @throw TypedValueException if stored value is not a list.
|
||||||
|
*/
|
||||||
|
TVector &ValueList();
|
||||||
|
|
||||||
|
const TVector &ValueList() const;
|
||||||
|
|
||||||
|
/** Check if the stored value is a list value */
|
||||||
|
bool IsList() const;
|
||||||
|
|
||||||
DECLARE_VALUE_AND_TYPE_GETTERS(value_map_t, Map)
|
DECLARE_VALUE_AND_TYPE_GETTERS(value_map_t, Map)
|
||||||
DECLARE_VALUE_AND_TYPE_GETTERS(VertexAccessor, Vertex)
|
DECLARE_VALUE_AND_TYPE_GETTERS(VertexAccessor, Vertex)
|
||||||
DECLARE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge)
|
DECLARE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge)
|
||||||
@ -358,7 +433,7 @@ class TypedValue {
|
|||||||
// complexity so it shouldn't be a problem. This is maybe even faster
|
// complexity so it shouldn't be a problem. This is maybe even faster
|
||||||
// because of data locality.
|
// because of data locality.
|
||||||
std::string string_v;
|
std::string string_v;
|
||||||
std::vector<TypedValue> list_v;
|
TVector list_v;
|
||||||
// clang doesn't allow unordered_map to have incomplete type as value so we
|
// clang doesn't allow unordered_map to have incomplete type as value so we
|
||||||
// we use map.
|
// we use map.
|
||||||
std::map<std::string, TypedValue> map_v;
|
std::map<std::string, TypedValue> map_v;
|
||||||
|
@ -266,9 +266,10 @@ auto GetProp(const RecordAccessor<TRecord> &rec, std::string prop,
|
|||||||
|
|
||||||
// Checks if the given path is actually a path from source to sink and if all
|
// Checks if the given path is actually a path from source to sink and if all
|
||||||
// of its edges exist in the given edge list.
|
// of its edges exist in the given edge list.
|
||||||
|
template <class TPathAllocator>
|
||||||
void CheckPath(database::GraphDbAccessor *dba, const VertexAccessor &source,
|
void CheckPath(database::GraphDbAccessor *dba, const VertexAccessor &source,
|
||||||
const VertexAccessor &sink,
|
const VertexAccessor &sink,
|
||||||
const std::vector<query::TypedValue> &path,
|
const std::vector<query::TypedValue, TPathAllocator> &path,
|
||||||
const std::vector<std::pair<int, int>> &edges) {
|
const std::vector<std::pair<int, int>> &edges) {
|
||||||
VertexAccessor curr = source;
|
VertexAccessor curr = source;
|
||||||
for (const auto &edge_tv : path) {
|
for (const auto &edge_tv : path) {
|
||||||
@ -276,7 +277,7 @@ void CheckPath(database::GraphDbAccessor *dba, const VertexAccessor &source,
|
|||||||
auto edge = edge_tv.ValueEdge();
|
auto edge = edge_tv.ValueEdge();
|
||||||
|
|
||||||
ASSERT_TRUE(edge.from() == curr || edge.to() == curr);
|
ASSERT_TRUE(edge.from() == curr || edge.to() == curr);
|
||||||
auto next = edge.from_is(curr) ? edge.to() : edge.from();
|
VertexAccessor next = edge.from_is(curr) ? edge.to() : edge.from();
|
||||||
|
|
||||||
int from = GetProp(curr, "id", dba).Value<int64_t>();
|
int from = GetProp(curr, "id", dba).Value<int64_t>();
|
||||||
int to = GetProp(next, "id", dba).Value<int64_t>();
|
int to = GetProp(next, "id", dba).Value<int64_t>();
|
||||||
|
@ -604,7 +604,8 @@ class QueryPlanExpandVariable : public testing::Test {
|
|||||||
Symbol symbol) {
|
Symbol symbol) {
|
||||||
map_int count_per_length;
|
map_int count_per_length;
|
||||||
for (const auto &edge_list :
|
for (const auto &edge_list :
|
||||||
GetResults<std::vector<TypedValue>>(input_op, symbol)) {
|
GetResults<std::vector<TypedValue, utils::Allocator<TypedValue>>>(
|
||||||
|
input_op, symbol)) {
|
||||||
auto length = edge_list.size();
|
auto length = edge_list.size();
|
||||||
auto found = count_per_length.find(length);
|
auto found = count_per_length.find(length);
|
||||||
if (found == count_per_length.end())
|
if (found == count_per_length.end())
|
||||||
|
@ -460,3 +460,36 @@ TEST_F(AllTypesFixture, AssignmentWithMemoryResource) {
|
|||||||
utils::NewDeleteResource());
|
utils::NewDeleteResource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||||
|
TEST_F(AllTypesFixture, PropagationOfMemoryOnConstruction) {
|
||||||
|
utils::MonotonicBufferResource monotonic_memory(1024);
|
||||||
|
std::vector<TypedValue, utils::Allocator<TypedValue>>
|
||||||
|
values_with_custom_memory(&monotonic_memory);
|
||||||
|
for (const auto &value : values_) {
|
||||||
|
EXPECT_EQ(value.GetMemoryResource(), utils::NewDeleteResource());
|
||||||
|
values_with_custom_memory.emplace_back(value);
|
||||||
|
const auto ©_constructed_value = values_with_custom_memory.back();
|
||||||
|
EXPECT_EQ(copy_constructed_value.GetMemoryResource(), &monotonic_memory);
|
||||||
|
TypedValue copy(values_with_custom_memory.back());
|
||||||
|
EXPECT_EQ(copy.GetMemoryResource(), utils::NewDeleteResource());
|
||||||
|
values_with_custom_memory.emplace_back(std::move(copy));
|
||||||
|
const auto &move_constructed_value = values_with_custom_memory.back();
|
||||||
|
EXPECT_EQ(move_constructed_value.GetMemoryResource(), &monotonic_memory);
|
||||||
|
if (value.type() == TypedValue::Type::List) {
|
||||||
|
ASSERT_EQ(move_constructed_value.type(), value.type());
|
||||||
|
const auto &original = value.ValueList();
|
||||||
|
const auto &moved = move_constructed_value.ValueList();
|
||||||
|
const auto &copied = copy_constructed_value.ValueList();
|
||||||
|
ASSERT_EQ(moved.size(), original.size());
|
||||||
|
ASSERT_EQ(copied.size(), original.size());
|
||||||
|
for (size_t i = 0; i < value.ValueList().size(); ++i) {
|
||||||
|
EXPECT_EQ(original[i].GetMemoryResource(), utils::NewDeleteResource());
|
||||||
|
EXPECT_EQ(moved[i].GetMemoryResource(), &monotonic_memory);
|
||||||
|
EXPECT_EQ(copied[i].GetMemoryResource(), &monotonic_memory);
|
||||||
|
EXPECT_TRUE(TypedValue::BoolEqual{}(original[i], moved[i]));
|
||||||
|
EXPECT_TRUE(TypedValue::BoolEqual{}(original[i], copied[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user