📄 auto_buffer.hpp
字号:
#else /* ? STLSOFT_LF_ALLOCATOR_ALLOCATE_HAS_HINT */
return get_allocator().allocate(cItems);
#endif /* STLSOFT_LF_ALLOCATOR_ALLOCATE_HAS_HINT */
}
void deallocate_(pointer p, size_type cItems)
{
STLSOFT_ASSERT(NULL != p);
#ifdef STLSOFT_LF_ALLOCATOR_DEALLOCATE_HAS_COUNT
get_allocator().deallocate(p, cItems);
#else /* ? STLSOFT_LF_ALLOCATOR_DEALLOCATE_HAS_COUNT */
STLSOFT_SUPPRESS_UNUSED(cItems);
get_allocator().deallocate(p);
#endif /* STLSOFT_LF_ALLOCATOR_DEALLOCATE_HAS_COUNT */
}
pointer reallocate_(pointer p, size_type cItems, size_type cNewItems)
{
pointer new_p = allocate_(cNewItems, p);
// This test is needed, since some allocators may not throw
// bad_alloc
if(NULL != new_p)
{
block_copy(new_p, p, cItems);
deallocate_(p, cItems);
}
return new_p;
}
protected:
static void block_copy(pointer dest, const_pointer src, size_type cItems)
{
pod_copy_n(dest, src, cItems);
}
static void block_set(pointer dest, size_type cItems, const_reference value)
{
pod_fill_n(dest, cItems, value);
}
/// @}
/// \name Construction
/// @{
public:
/// \brief Constructs an auto_buffer with the given number of elements
///
/// Constructs an auto_buffer with the given number of elements. If the
/// allocation fails by throwing an exception, that exception is passed
/// through to the caller. If allocation fails by returning a null
/// pointer the auto_buffer instance is correctly constructed, and the
/// \link #size size() \endlink method returns 0.
///
/// \see \link #size size() \endlink
/// \param cItems The number of items in the constructed instance
ss_explicit_k auto_buffer(size_type cItems)
: m_buffer((space < cItems) ? allocate_(cItems) : const_cast<pointer>(&m_internal[0]))
, m_cItems((NULL != m_buffer) ? cItems : 0)
, m_bExternal(space < cItems)
{
// Can't create one with an empty buffer. Though such is not legal
// it is supported by some compilers, so we must ensure it cannot be
// so
STLSOFT_STATIC_ASSERT(0 != space);
// These assertions ensure that the member ordering is not
// changed, invalidating the initialisation logic of m_buffer and
// m_cItems. The runtime assert is included for those compilers that
// do not implement compile-time asserts.
#ifdef STLSOFT_CF_USE_RAW_OFFSETOF_IN_STATIC_ASSERT
STLSOFT_STATIC_ASSERT(STLSOFT_RAW_OFFSETOF(class_type, m_buffer) < STLSOFT_RAW_OFFSETOF(class_type, m_cItems));
#endif /* STLSOFT_CF_USE_RAW_OFFSETOF_IN_STATIC_ASSERT */
STLSOFT_MESSAGE_ASSERT("m_buffer must be before m_cItems in the auto_buffer definition", stlsoft_reinterpret_cast(ss_byte_t*, &m_buffer) < stlsoft_reinterpret_cast(ss_byte_t*, &m_cItems));
#ifndef _STLSOFT_AUTO_BUFFER_ALLOW_NON_POD
// Use the must_be_pod constraint to ensure that
// no type is managed in auto_buffer which would result in
// dangerous mismanagement of the lifetime of its instances.
//
// Preprocessor specification of _STLSOFT_AUTO_BUFFER_ALLOW_NON_POD
// prevents this, but the natural rules of the language will
// still prevent non POD types being placed in m_internal[].
stlsoft_constraint_must_be_pod(value_type);
#endif /* _STLSOFT_AUTO_BUFFER_ALLOW_NON_POD */
STLSOFT_ASSERT(is_valid());
}
/// \brief Releases the allocated element array
///
/// Releases any allocated memory. If the internal memory buffer was
/// used, then nothing is done, otherwise the allocated memory is
/// returned to the allocator.
#if defined(STLSOFT_CF_EXCEPTION_SIGNATURE_SUPPORT)
~auto_buffer()
#else /* ? STLSOFT_CF_EXCEPTION_SIGNATURE_SUPPORT */
~auto_buffer() stlsoft_throw_0()
#endif /* STLSOFT_CF_EXCEPTION_SIGNATURE_SUPPORT */
{
STLSOFT_ASSERT(is_valid());
if(is_in_external_array_())
{
STLSOFT_ASSERT(NULL != m_buffer);
STLSOFT_ASSERT(m_bExternal);
STLSOFT_ASSERT(&m_internal[0] != m_buffer);
deallocate_(m_buffer, m_cItems);
}
}
/// @}
/// \name Operations
/// @{
private:
// Policy functions
ss_bool_t is_in_external_array_() const
{
#if defined(STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK)
// Old implementation always uses internal array if size() <= internal_size()
STLSOFT_ASSERT((space < m_cItems) == (m_buffer != &m_internal[0]));
return space < m_cItems;
#else /* ? STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK */
// Old implementation always uses internal array if size() <= internal_size()
// STLSOFT_ASSERT((m_buffer != &m_internal[0]) || !(space < m_cItems));
STLSOFT_ASSERT((m_buffer != &m_internal[0]) == m_bExternal);
STLSOFT_ASSERT(m_bExternal || !(space < m_cItems));
// return m_buffer != &m_internal[0];
return m_bExternal;
#endif /* STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK */
}
public:
/// \brief Expands or contracts the number of items in the buffer
///
/// \param cItems The number of items to change in the buffer
/// \return Returns \c true if successful. Function failure occurs when sufficient storage for the
/// requested items cannot be allocated. In that case, std::bad_alloc will be throw for allocators
/// that support it, otherwise the function will return \c false. In either case, the original
/// storage and contents of the buffer will remain unchanged.
///
/// \note When reducing the number of elements, the implementation favours speed above memory
/// consumption. If the new item size is still larger than the internal storage size
/// (\c internal_size()) then the heap allocated block will not be changed (i.e. it will not be
/// exchanged for a smaller block).
///
/// \note As from STLSoft version 1.9, the external array is not discarded in favour of the internal
/// array when 0 < cItems < internal_size(). Only resize(0) will deallocate the external array.
ss_bool_t resize(size_type cItems)
{
STLSOFT_ASSERT(is_valid());
// There are four changes possible:
//
// 1. Expansion within the internal buffer
// 2. Contraction within the internal buffer
// 3. Expansion from the internal buffer to an allocated buffer
// 4. Contraction from an allocated buffer to the internal buffer
// 4.a Where n is 0, or when STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK is defined
// 4.b Where 0 < n <= internal_size() - this is new behaviour - we do not go to the internal array
// 5. Expansion from the allocated buffer to another allocated buffer
// 6. Contraction from the allocated buffer to another allocated buffer
if(m_cItems < cItems)
{
// Expansion; cases 1, 3 & 5
if(is_in_external_array_())
{
// Current buffer is allocated: case 5
pointer new_buffer = reallocate_(m_buffer, m_cItems, cItems);
// Still test for NULL here, since some allocators will
// not throw bad_alloc.
if(NULL == new_buffer)
{
return false;
}
// Now repoint to the new buffer
m_buffer = new_buffer;
}
else
{
// Expanding from internal buffer; cases 1 & 3
if(space < cItems)
{
// Expanding to allocated buffer; case 3
pointer new_buffer = allocate_(cItems);
// Still test for NULL here, since some allocators will
// not throw bad_alloc.
if(NULL == new_buffer)
{
return false;
}
block_copy(new_buffer, m_buffer, m_cItems);
m_buffer = new_buffer;
m_bExternal = true;
}
else
{
// Expanding to internal buffer; case 1
// Nothing to do
STLSOFT_ASSERT(!(space < cItems));
}
}
}
else
{
// Contraction; cases 2, 4 & 6
if(is_in_external_array_())
{
// Current buffer is allocated: cases 4 & 6
if(space < cItems)
{
// Contracting within allocated buffer; case 6
// Nothing to do
STLSOFT_ASSERT(space < cItems);
}
#if defined(STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK)
else
#else /* ? STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK */
else if(0 == cItems)
#endif /* STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK */
{
// Contracting back to internal; case 4
block_copy(const_cast<pointer>(&m_internal[0]), m_buffer, cItems);
deallocate_(m_buffer, m_cItems);
m_buffer = const_cast<pointer>(&m_internal[0]);
m_bExternal = false;
}
}
else
{
// Current buffer is internal; case 2
// Nothing to do
STLSOFT_ASSERT(!(space < cItems));
}
}
m_cItems = cItems;
STLSOFT_ASSERT(is_valid());
return true;
}
/// \brief Swaps contents with the given buffer
///
/// \note This method is only constant time when the memory for two buffers
/// has been acquired via the allocator. Otherwise, it will depend on the
/// costs of exchanging the memory
///
/// \note Exception-safety: Provides the no-throw guarantee
void swap(class_type &rhs) stlsoft_throw_0()
{
STLSOFT_ASSERT(is_valid());
if( is_in_external_array_() &&
rhs.is_in_external_array_())
{
// Both are allocated, so just swap them
std_swap(m_buffer, rhs.m_buffer);
}
else if(is_in_external_array_())
{
// *this is allocated on the heap, rhs is using m_internal
// 1. Copy the contents of rhs.m_internal to this->m_internal
block_copy(&m_internal[0], &rhs.m_internal[0], rhs.m_cItems);
// 2. Move m_buffer from *this to rhs
rhs.m_buffer = m_buffer;
// 3. Tell *this to use its internal buffer
m_buffer = &m_internal[0];
}
else if(rhs.is_in_external_array_())
{
// This is a lazy cheat, but eminently effective.
rhs.swap(*this);
return;
}
else
{
// Both are using internal buffers, so we exchange the contents
value_type t[space];
block_copy(&t[0], &rhs.m_internal[0], rhs.m_cItems);
block_copy(&rhs.m_internal[0], &m_internal[0], m_cItems);
block_copy(&m_internal[0], &t[0], rhs.m_cItems);
}
std_swap(m_cItems, rhs.m_cItems);
std_swap(m_bExternal, rhs.m_bExternal);
STLSOFT_ASSERT(is_valid());
}
/// @}
/// \name Operators
/// @{
public:
// Note: The following two const and non-const implicit conversion
// operators are correctly implemented. However, GCC will pedantically
// give a verbose warning describing its having selected one over the
// other, and this is, in current versions of the compiler, not
// suppressable. The warnings must, alas, simply be ignored.
#ifdef _STLSOFT_AUTO_BUFFER_ALLOW_NON_CONST_CONVERSION_OPERATOR
/// \brief An implicit conversion to a pointer to the start of the element array
///
/// \deprecate This is deprecated
operator pointer ()
{
STLSOFT_ASSERT(is_valid());
return m_buffer;
}
#else /* ? _STLSOFT_AUTO_BUFFER_ALLOW_NON_CONST_CONVERSION_OPERATOR */
/// \brief Subscript operator
reference operator [](size_type index)
{
STLSOFT_MESSAGE_ASSERT("Index is out of bounds", index <= m_cItems);
STLSOFT_ASSERT(is_valid());
return m_buffer[index];
}
/// \brief Subscript operator
const_reference operator [](size_type index) const
{
STLSOFT_MESSAGE_ASSERT("Index is out of bounds", index <= m_cItems);
STLSOFT_ASSERT(is_valid());
return m_buffer[index];
}
#endif /* _STLSOFT_AUTO_BUFFER_ALLOW_NON_CONST_CONVERSION_OPERATOR */
#ifdef _STLSOFT_AUTO_BUFFER_ALLOW_CONST_CONVERSION_OPERATOR
/// \brief An implicit conversion to a pointer-to-const to the start of the element array
operator const_pointer () const
{
STLSOFT_ASSERT(is_valid());
return m_buffer;
}
#endif /* _STLSOFT_AUTO_BUFFER_ALLOW_CONST_CONVERSION_OPERATOR */
/// @}
/// \name Accessors
/// @{
public:
/// \brief Returns a pointer to the element array
pointer data()
{
STLSOFT_ASSERT(is_valid());
return m_buffer;
}
/// \brief Returns a pointer-to-const to the element array
const_pointer data() const
{
STLSOFT_ASSERT(is_valid());
return m_buffer;
}
/// @}
/// \name Iteration
/// @{
public:
/// \brief Returns a non-mutating iterator representing the start of the sequence
const_iterator begin() const
{
STLSOFT_ASSERT(is_valid());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -