323 lines
9.2 KiB
C++
323 lines
9.2 KiB
C++
#ifndef INPLACE_VECTOR_INCLUDED
|
|
#define INPLACE_VECTOR_INCLUDED
|
|
|
|
/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
|
|
|
|
|
|
/* This file defines the Inplace_vector class template. */
|
|
|
|
#include "my_config.h"
|
|
#include <vector>
|
|
#include "my_dbug.h"
|
|
#include "my_sys.h"
|
|
#include "mysql/psi/psi_memory.h"
|
|
|
|
|
|
/**
|
|
Utility container class to store elements stably and scalably.
|
|
The address of an element stored in the container is stable as long as
|
|
the object is alive, no object is copy-constructed/reassigned by push_back
|
|
operations once it's stored into this container. And users of such containers
|
|
*can* assign to elements stored in the container just like using std::vector.
|
|
|
|
It is similar to STL vector but it is uniquely suitable in below situation:
|
|
whenever stable element address, or element copy construction/assignement
|
|
behaviors are forbidden. It only has a limited subset of the std::vector
|
|
interface, and especially it doesn't have an iterator interface or element
|
|
elimination interface, we don't need them for now. And this container
|
|
is not multi-threading safe. It uses my_malloc/my_free
|
|
to allocate/free memory arrays and caller can pass PSI key.
|
|
|
|
The container keeps a collection of arrays, each of which has a fixed
|
|
NO. of slots to store elements. When one array is full, another is appended.
|
|
When the vector shrinks at tail, useless arrays are removed and its memory
|
|
space released.
|
|
|
|
@tparam objtype The type of the elements to store.
|
|
@tparam array_size The NO. of element slots in each array.
|
|
*/
|
|
template <typename objtype, size_t array_size= 16>
|
|
class Inplace_vector
|
|
{
|
|
private:
|
|
std::vector<objtype *> m_obj_arrays;
|
|
PSI_memory_key m_psi_key;
|
|
size_t m_obj_count;
|
|
bool m_outof_mem;
|
|
|
|
/**
|
|
Return an existing used slot, or append exactly one slot at end of the last
|
|
array and return it. If the last array is already full before the
|
|
append, allocate a new array then allocate one slot and return it.
|
|
@param index The index of the element slot to return. It must be
|
|
one within valid in-use range of the vector, or be equal to
|
|
the size of the vector.
|
|
@return the object pointer stored in the specified slot; NULL if need
|
|
to allocate more space but out of memory.
|
|
*/
|
|
objtype *get_space(size_t index)
|
|
{
|
|
DBUG_ASSERT(index <= m_obj_count);
|
|
size_t arr_id= index / array_size;
|
|
size_t slot_id= index % array_size;
|
|
objtype *ptr= NULL;
|
|
|
|
DBUG_ASSERT(arr_id <= m_obj_arrays.size());
|
|
|
|
// Appending a new slot causes appending a new array.
|
|
if (arr_id == m_obj_arrays.size())
|
|
{
|
|
DBUG_ASSERT(slot_id == 0);
|
|
if (m_outof_mem)
|
|
return NULL;
|
|
append_new_array();
|
|
if (m_outof_mem)
|
|
return NULL;
|
|
}
|
|
|
|
ptr= m_obj_arrays[arr_id];
|
|
ptr+= slot_id;
|
|
return ptr;
|
|
}
|
|
|
|
void append_new_array()
|
|
{
|
|
if (m_outof_mem)
|
|
return;
|
|
|
|
void *p= my_malloc(m_psi_key, sizeof(objtype) * array_size, MYF(MY_FAE));
|
|
|
|
try
|
|
{
|
|
m_obj_arrays.push_back(static_cast<objtype *>(p));
|
|
}
|
|
catch (...)
|
|
{
|
|
m_outof_mem= true;
|
|
my_free(p);
|
|
}
|
|
}
|
|
|
|
Inplace_vector(const Inplace_vector &);
|
|
Inplace_vector &operator=(const Inplace_vector &rhs);
|
|
public:
|
|
|
|
explicit Inplace_vector(PSI_memory_key psi_key)
|
|
:m_psi_key(psi_key), m_outof_mem(false)
|
|
{
|
|
m_obj_count= 0;
|
|
append_new_array();
|
|
}
|
|
|
|
/**
|
|
Release memory space and destroy all contained objects.
|
|
*/
|
|
~Inplace_vector()
|
|
{
|
|
delete_all_objects();
|
|
}
|
|
|
|
/**
|
|
Get an existing element's pointer, index must be in [0, m_obj_count).
|
|
@param index The index of the element to return. It must be
|
|
within valid in-use range of the vector.
|
|
@return The element address specified by index; NULL if out of memory.
|
|
*/
|
|
objtype *get_object(size_t index)
|
|
{
|
|
DBUG_ASSERT(index < m_obj_count);
|
|
return get_space(index);
|
|
}
|
|
|
|
/**
|
|
Allocate space for an object, and construct it using its default
|
|
constructor, and return its address.
|
|
@return the appended object's address; NULL if out of memory.
|
|
*/
|
|
objtype *append_object()
|
|
{
|
|
// Use placement new operator to construct this object at proper
|
|
// location.
|
|
return ::new (get_space(m_obj_count++)) objtype;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::push_back interface. It's guaranteed that existing
|
|
elements stored in the vector is never copy constructed/reassigned
|
|
by this operation. When the last element array is full, a new one is
|
|
allocated and tracked.
|
|
|
|
@param obj The element to store into the vector.
|
|
@return The appended object stored in the container; NULL if out of memory.
|
|
*/
|
|
objtype *push_back(const objtype &obj)
|
|
{
|
|
// Use placement new operator to construct this object at proper
|
|
// location.
|
|
return ::new (get_space(m_obj_count++)) objtype(obj);
|
|
}
|
|
|
|
/**
|
|
STL std::vector::resize interface. Has identical behavior as STL
|
|
std::vector::resize except that no element copy construction or
|
|
reassignment is ever caused by this operation.
|
|
|
|
@param val default value assigned to extended slots in the vector. Unused
|
|
if the vector is shrinked. We have to define a const reference
|
|
instead of passing by value because MSVC on 32bit Windows
|
|
doesn't allow formal parameter to have alignment specification
|
|
(error C2719) as defined in my_aligned_storage but in
|
|
Geometry_buffer and potentially more classes in future,
|
|
we do use alignement specification.
|
|
@return true if out of memory; false if successful.
|
|
*/
|
|
bool resize(size_t new_size, const objtype &val= objtype())
|
|
{
|
|
if (new_size > size())
|
|
{
|
|
for (size_t i= size(); i < new_size; i++)
|
|
{
|
|
if (push_back(val) == NULL)
|
|
return true;
|
|
}
|
|
}
|
|
else if (new_size < size())
|
|
{
|
|
// Destroy objects at tail.
|
|
for (size_t i= new_size; i < size(); i++)
|
|
get_object(i)->~objtype();
|
|
|
|
// Free useless array space.
|
|
for (size_t j= new_size / array_size + 1;
|
|
j < m_obj_arrays.size(); j++)
|
|
my_free(m_obj_arrays[j]);
|
|
|
|
m_obj_count= new_size;
|
|
m_obj_arrays.resize(new_size / array_size + 1);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::size interface.
|
|
@return the number of elements effectively stored in the vector.
|
|
*/
|
|
size_t size() const
|
|
{
|
|
return m_obj_count;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::capacity interface.
|
|
@return the max number of element that can be stored into this vector
|
|
without growing its size.
|
|
*/
|
|
size_t capacity() const
|
|
{
|
|
return m_obj_arrays.size() * array_size;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::empty interface.
|
|
@return whether size() == 0.
|
|
*/
|
|
bool empty() const
|
|
{
|
|
return m_obj_count == 0;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::clear interface.
|
|
Destroy all elements (by calling each element's destructor) stored in
|
|
the vector, and then release all memory held by it.
|
|
*/
|
|
void clear()
|
|
{
|
|
delete_all_objects();
|
|
}
|
|
|
|
/**
|
|
STL std::vector::back interface.
|
|
@return the reference of the last object stored in the vector.
|
|
*/
|
|
const objtype &back() const
|
|
{
|
|
DBUG_ASSERT(size() > 0);
|
|
objtype *p= get_object(size() - 1);
|
|
return *p;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::back interface.
|
|
@return the reference of the last object stored in the vector.
|
|
*/
|
|
objtype &back()
|
|
{
|
|
DBUG_ASSERT(size() > 0);
|
|
objtype *p= get_object(size() - 1);
|
|
return *p;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::operator[] interface.
|
|
@param i The index of the element to return. It must be
|
|
within valid in-use range of the vector.
|
|
@return The element reference specified by index.
|
|
*/
|
|
const objtype &operator[](size_t i) const
|
|
{
|
|
DBUG_ASSERT(i < size());
|
|
objtype *p= get_object(i);
|
|
return *p;
|
|
}
|
|
|
|
/**
|
|
STL std::vector::operator[] interface.
|
|
@param i The index of the element to return. It must be
|
|
within valid in-use range of the vector.
|
|
@return The element reference specified by index.
|
|
*/
|
|
objtype &operator[](size_t i)
|
|
{
|
|
DBUG_ASSERT(i < size());
|
|
objtype *p= get_object(i);
|
|
return *p;
|
|
}
|
|
|
|
/**
|
|
Destroy all elements (by calling each element's destructor) stored in
|
|
the vector, and then release all memory held by it.
|
|
*/
|
|
void delete_all_objects()
|
|
{
|
|
// Call each element's destructor.
|
|
for (size_t i= 0; i < size(); i++)
|
|
{
|
|
objtype *p= get_object(i);
|
|
p->~objtype();
|
|
}
|
|
for (size_t i= 0; i < m_obj_arrays.size(); ++i)
|
|
my_free(m_obj_arrays[i]);
|
|
|
|
m_obj_arrays.clear();
|
|
m_obj_count= 0;
|
|
}
|
|
|
|
};
|
|
|
|
#endif // !INPLACE_VECTOR_INCLUDED
|