Don't copy aggregate function states with memcpy.

They may contain pointers to themselves, such as when using
PODArrayWithStackMemory. Instead, initialize empty new structures
with `create`, and merge the old one to it with `merge`.
This commit is contained in:
Alexander Kuzmenkov 2019-06-28 21:42:14 +03:00
parent f6ee2ea4e4
commit bd33bf8f96

View File

@ -53,42 +53,49 @@ private:
{
AggregateFunctionForEachData & state = data(place);
/// Ensure we have aggreate states for new_size elements, allocate from arena if needed
/// Ensure we have aggreate states for new_size elements, allocate
/// from arena if needed. When reallocating, we can't copy the
/// states to new buffer with memcpy, because they may contain pointers
/// to themselves. In particular, this happens when a state contains
/// a PODArrayWithStackMemory, which stores small number of elements
/// inline. This is why we create new empty states in the new buffer,
/// and merge the old states to them.
size_t old_size = state.dynamic_array_size;
if (old_size < new_size)
{
state.array_of_aggregate_datas = arena.alignedRealloc(
state.array_of_aggregate_datas,
old_size * nested_size_of_data,
char * old_state = state.array_of_aggregate_datas;
char * new_state = arena.alignedAlloc(
new_size * nested_size_of_data,
nested_func->alignOfData());
size_t i = old_size;
char * nested_state = state.array_of_aggregate_datas + i * nested_size_of_data;
size_t i;
try
{
for (; i < new_size; ++i)
for (i = 0; i < new_size; ++i)
{
nested_func->create(nested_state);
nested_state += nested_size_of_data;
nested_func->create(&new_state[i * nested_size_of_data]);
}
}
catch (...)
{
size_t cleanup_size = i;
nested_state = state.array_of_aggregate_datas + i * nested_size_of_data;
for (i = 0; i < cleanup_size; ++i)
{
nested_func->destroy(nested_state);
nested_state += nested_size_of_data;
nested_func->destroy(&new_state[i * nested_size_of_data]);
}
throw;
}
for (i = 0; i < old_size; i++)
{
nested_func->merge(&new_state[i * nested_size_of_data],
&old_state[i * nested_size_of_data],
&arena);
}
state.array_of_aggregate_datas = new_state;
state.dynamic_array_size = new_size;
}