Field: fixed memory leak [#CLICKHOUSE-2756].

This commit is contained in:
Alexey Milovidov 2017-01-20 06:33:56 +03:00
parent 6226132be0
commit 7fa73c5462

View File

@ -79,6 +79,7 @@ public:
} }
}; };
/// Позволяет получить идентификатор для типа или наоборот. /// Позволяет получить идентификатор для типа или наоборот.
template <typename T> struct TypeToEnum; template <typename T> struct TypeToEnum;
template <Types::Which which> struct EnumToType; template <Types::Which which> struct EnumToType;
@ -99,7 +100,7 @@ public:
Field(Field && rhs) Field(Field && rhs)
{ {
move(std::move(rhs)); create(std::move(rhs));
} }
template <typename T> template <typename T>
@ -108,6 +109,12 @@ public:
create(rhs); create(rhs);
} }
template <typename T>
Field(T && rhs)
{
create(std::move(rhs));
}
/// Создать строку inplace. /// Создать строку inplace.
Field(const char * data, size_t size) Field(const char * data, size_t size)
{ {
@ -119,6 +126,7 @@ public:
create(data, size); create(data, size);
} }
/// NOTE In case when field already has string type, more direct assign is possible.
void assignString(const char * data, size_t size) void assignString(const char * data, size_t size)
{ {
destroy(); destroy();
@ -135,8 +143,13 @@ public:
{ {
if (this != &rhs) if (this != &rhs)
{ {
destroy(); if (which != rhs.which)
create(rhs); {
destroy();
create(rhs);
}
else
assign(rhs); /// This assigns string or vector without deallocation of existing buffer.
} }
return *this; return *this;
} }
@ -145,7 +158,13 @@ public:
{ {
if (this != &rhs) if (this != &rhs)
{ {
move(std::move(rhs)); if (which != rhs.which)
{
destroy();
create(std::move(rhs));
}
else
assign(std::move(rhs));
} }
return *this; return *this;
} }
@ -153,8 +172,28 @@ public:
template <typename T> template <typename T>
Field & operator= (const T & rhs) Field & operator= (const T & rhs)
{ {
destroy(); if (which != TypeToEnum<T>::value)
create(rhs); {
destroy();
create(rhs);
}
else
assign(rhs);
return *this;
}
template <typename T>
Field & operator= (T && rhs)
{
if (which != TypeToEnum<T>::value)
{
destroy();
create(std::move(rhs));
}
else
assign(std::move(rhs));
return *this; return *this;
} }
@ -289,6 +328,7 @@ private:
Types::Which which; Types::Which which;
/// Assuming there was no allocated state or it was deallocated (see destroy).
template <typename T> template <typename T>
void create(const T & x) void create(const T & x)
{ {
@ -297,25 +337,70 @@ private:
new (ptr) T(x); new (ptr) T(x);
} }
void create(const Null & x) template <typename T>
void create(T && x)
{ {
which = Types::Null; which = TypeToEnum<T>::value;
T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(storage);
new (ptr) T(std::move(x));
} }
/// Assuming same types.
template <typename T>
void assign(const T & x)
{
T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(storage);
*ptr = x;
}
template <typename T>
void assign(T && x)
{
T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(storage);
*ptr = std::move(x);
}
template <typename F, typename Field> /// Field template parameter may be const or non-const Field.
static void dispatch(F && f, Field & field)
{
switch (field.which)
{
case Types::Null: f(field.get<Null>()); return;
case Types::UInt64: f(field.get<UInt64>()); return;
case Types::Int64: f(field.get<Int64>()); return;
case Types::Float64: f(field.get<Float64>()); return;
case Types::String: f(field.get<String>()); return;
case Types::Array: f(field.get<Array>()); return;
case Types::Tuple: f(field.get<Tuple>()); return;
default:
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
}
}
void create(const Field & x) void create(const Field & x)
{ {
switch (x.which) dispatch([this] (auto & value) { create(value); }, x);
{
case Types::Null: create(Null()); break;
case Types::UInt64: create(x.get<UInt64>()); break;
case Types::Int64: create(x.get<Int64>()); break;
case Types::Float64: create(x.get<Float64>()); break;
case Types::String: create(x.get<String>()); break;
case Types::Array: create(x.get<Array>()); break;
case Types::Tuple: create(x.get<Tuple>()); break;
}
} }
void create(Field && x)
{
dispatch([this] (auto & value) { create(std::move(value)); }, x);
}
void assign(const Field & x)
{
dispatch([this] (auto & value) { assign(value); }, x);
}
void assign(Field && x)
{
dispatch([this] (auto & value) { assign(std::move(value)); }, x);
}
void create(const char * data, size_t size) void create(const char * data, size_t size)
{ {
which = Types::String; which = Types::String;
@ -347,6 +432,8 @@ private:
default: default:
break; break;
} }
which = Types::Null; /// for exception safety in subsequent calls to destroy and create, when create fails.
} }
template <typename T> template <typename T>
@ -355,31 +442,6 @@ private:
T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(storage); T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(storage);
ptr->~T(); ptr->~T();
} }
template <typename T>
void moveValue(Field && x)
{
T * __attribute__((__may_alias__)) ptr_this = reinterpret_cast<T*>(storage);
T * __attribute__((__may_alias__)) ptr_x = reinterpret_cast<T*>(x.storage);
new (ptr_this) T(std::move(*ptr_x));
}
void move(Field && x)
{
which = x.which;
switch (x.which)
{
case Types::Null: create(Null()); break;
case Types::UInt64: create(x.get<UInt64>()); break;
case Types::Int64: create(x.get<Int64>()); break;
case Types::Float64: create(x.get<Float64>()); break;
case Types::String: moveValue<String>(std::move(x)); break;
case Types::Array: moveValue<Array>(std::move(x)); break;
case Types::Tuple: moveValue<Tuple>(std::move(x)); break;
}
}
}; };
#undef DBMS_MIN_FIELD_SIZE #undef DBMS_MIN_FIELD_SIZE
@ -393,7 +455,7 @@ template <> struct Field::TypeToEnum<String> { static const Types::Which
template <> struct Field::TypeToEnum<Array> { static const Types::Which value = Types::Array; }; template <> struct Field::TypeToEnum<Array> { static const Types::Which value = Types::Array; };
template <> struct Field::TypeToEnum<Tuple> { static const Types::Which value = Types::Tuple; }; template <> struct Field::TypeToEnum<Tuple> { static const Types::Which value = Types::Tuple; };
template <> struct Field::EnumToType<Field::Types::Null> { using Type = Null ; }; template <> struct Field::EnumToType<Field::Types::Null> { using Type = Null ; };
template <> struct Field::EnumToType<Field::Types::UInt64> { using Type = UInt64 ; }; template <> struct Field::EnumToType<Field::Types::UInt64> { using Type = UInt64 ; };
template <> struct Field::EnumToType<Field::Types::Int64> { using Type = Int64 ; }; template <> struct Field::EnumToType<Field::Types::Int64> { using Type = Int64 ; };
template <> struct Field::EnumToType<Field::Types::Float64> { using Type = Float64 ; }; template <> struct Field::EnumToType<Field::Types::Float64> { using Type = Float64 ; };