/* ///////////////////////////////////////////////////////////////////////// * File: stlsoft/memory/auto_buffer.hpp (originally MTLocBfr.h, ::SynesisStl) * * Purpose: Contains the auto_buffer template class. * * Created: 19th January 2002 * Updated: 21st June 2010 * * Thanks: To Thorsten Ottosen for pointing out that allocators were * not swapped. * * Home: http://stlsoft.org/ * * Copyright (c) 2002-2010, Matthew Wilson and Synesis Software * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the * names of any contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ////////////////////////////////////////////////////////////////////// */ /** \file stlsoft/memory/auto_buffer.hpp * * \brief [C++ only] Definition of the stlsoft::auto_buffer class template * (\ref group__library__memory "Memory" Library). */ #ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER #define STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define STLSOFT_VER_STLSOFT_MEMORY_HPP_AUTO_BUFFER_MAJOR 5 # define STLSOFT_VER_STLSOFT_MEMORY_HPP_AUTO_BUFFER_MINOR 2 # define STLSOFT_VER_STLSOFT_MEMORY_HPP_AUTO_BUFFER_REVISION 4 # define STLSOFT_VER_STLSOFT_MEMORY_HPP_AUTO_BUFFER_EDIT 163 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /* ///////////////////////////////////////////////////////////////////////// * Compatibility */ /* [Incompatibilies-start] STLSOFT_COMPILER_IS_GCC: __GNUC__ < 3 [Incompatibilies-end] [DocumentationStatus:Ready] */ /* ///////////////////////////////////////////////////////////////////////// * Includes */ #ifndef STLSOFT_INCL_STLSOFT_H_STLSOFT # include #endif /* !STLSOFT_INCL_STLSOFT_H_STLSOFT */ #ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_FEATURES # include // for STLSOFT_LF_ALLOCATOR_ALLOCATE_HAS_HINT #endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_FEATURES */ #ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_SELECTOR # include #endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_SELECTOR */ #ifndef STLSOFT_INCL_STLSOFT_ALGORITHMS_HPP_POD # include #endif /* !STLSOFT_INCL_STLSOFT_HPP_ALGORITHMS_POD */ #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP # include #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP */ #ifdef _STLSOFT_AUTO_BUFFER_ALLOW_UDT # define _STLSOFT_AUTO_BUFFER_ALLOW_NON_POD # ifdef STLSOFT_CF_PRAGMA_MESSAGE_SUPPORT # pragma message("_STLSOFT_AUTO_BUFFER_ALLOW_UDT is deprecated. Use _STLSOFT_AUTO_BUFFER_ALLOW_NON_POD instead") # endif /* STLSOFT_CF_PRAGMA_MESSAGE_SUPPORT */ #endif /* _STLSOFT_AUTO_BUFFER_ALLOW_UDT */ #ifndef STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER # include #endif /* !STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER */ #ifndef _STLSOFT_AUTO_BUFFER_ALLOW_NON_POD # ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS # include # endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_CONSTRAINTS */ #endif /* _STLSOFT_AUTO_BUFFER_ALLOW_NON_POD */ #ifndef STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS # include #endif /* !STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS */ #ifdef STLSOFT_UNITTEST # include # if defined(STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT) # include # endif /* STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */ # include #endif /* STLSOFT_UNITTEST */ /* ///////////////////////////////////////////////////////////////////////// * Namespace */ #ifndef _STLSOFT_NO_NAMESPACE namespace stlsoft { #endif /* _STLSOFT_NO_NAMESPACE */ /* ///////////////////////////////////////////////////////////////////////// * Classes */ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # if !defined(STLSOFT_COMPILER_IS_BORLAND) && \ !defined(STLSOFT_COMPILER_IS_DMC) struct auto_buffer_internal_default { enum { min_value = 32 }; enum { max_value = 256 }; enum { division_factor = 2 }; }; template struct auto_buffer_internal_size_calculator : private auto_buffer_internal_default { private: // Stupid, stupid, stupid GCC requires them all to share the same // enum, which totally sucks. It whinges about comparisons between // enumerals (sic.) of different types. Thankfully it's irrelevant // because they're private enum { min_value = auto_buffer_internal_default::min_value , max_value = auto_buffer_internal_default::max_value , division_factor = auto_buffer_internal_default::division_factor , divided_value_ = static_cast((division_factor * max_value) / sizeof(T)) , divided_value = (max_value < divided_value_) ? max_value : divided_value_ }; public: enum { value = 1 == sizeof(T) ? max_value : divided_value < min_value ? min_value : divided_value }; }; STLSOFT_TEMPLATE_SPECIALISATION struct auto_buffer_internal_size_calculator { enum { value = auto_buffer_internal_default::max_value }; }; # if defined(STLSOFT_CF_NATIVE_WCHAR_T_SUPPORT) || \ defined(STLSOFT_CF_TYPEDEF_WCHAR_T_SUPPORT) STLSOFT_TEMPLATE_SPECIALISATION struct auto_buffer_internal_size_calculator { enum { value = auto_buffer_internal_default::max_value }; }; # endif /* STLSOFT_CF_NATIVE_WCHAR_T_SUPPORT */ # endif /* compiler */ #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ // class auto_buffer // /** \brief This class provides an efficient variable automatic buffer * * \ingroup group__library__memory * * \param T The type of the elements in the array * \param SPACE The number of elements in the array. For translators that * support default template arguments, this is defaulted to 256 * \param A The allocator type. Defaults to * \link stlsoft::allocator_selector allocator_selector::allocator_type\endlink * for translators that support default template arguments. * * This class provides an efficient replacement for dynamic memory block * allocation when the block size generally falls under a predictable limit. In * such cases, significant performance benefits can be achieved by using an * instance of a parameterisation of auto_buffer, whose size parameter SPACE * is set to a level to cater for most of the requested sizes. Only where * the size of the buffer needs to be larger than this limit does an * allocation occur from the heap/free-store via the given allocator. * * Using auto_buffer means one can avoid use of heap memory in * circumstances where stack memory is unsuitable, i.e. where there is no * maximum size to a memory requirement, or the maximum size is potentially * very large (and considerably larger than the median size). Consider the * following code extract from the core of the * Pantheios logging library: \code int pantheios_log_n( pan_sev_t severity , size_t numSlices , pan_slice_t const* slices) { typedef stlsoft::auto_buffer buffer_t; // Calculate the total size of the log statement, by summation of the slice array const size_t n = std::accumulate(stlsoft::member_selector(slices, &pan_slice_t::len) , stlsoft::member_selector(slices + numSlices, &pan_slice_t::len) , size_t(0)); buffer_t buffer(1 + n); . . . \endcode * * This use of auto_buffer illustrates two important features: * - there is no (compile-time) limit on the maximum size of a log statement * - memory is only allocated from the heap in the case where the total statement length >= 2047 bytes. * * Without auto_buffer, we would have three choices, all bad: * * 1. We could go to the heap in all cases: \code int pantheios_log_n( pan_sev_t severity , size_t numSlices , pan_slice_t const* slices) { typedef stlsoft::vector buffer_t; // Calculate the total size of the log statement, by summation of the slice array const size_t n = std::accumulate(stlsoft::member_selector(slices, &pan_slice_t::len) , stlsoft::member_selector(slices + numSlices, &pan_slice_t::len) , size_t(0)); buffer_t buffer(1 + n); . . . \endcode * But this would have an unacceptable performance hit (since the vast * majority of log statements are less than 2K in extent). * * 2. We could use a stack buffer, and truncate any log statement exceeding * the limit: \code int pantheios_log_n( pan_sev_t severity , size_t numSlices , pan_slice_t const* slices) { // Calculate the total size of the log statement, by summation of the slice array const size_t n = std::accumulate(stlsoft::member_selector(slices, &pan_slice_t::len) , stlsoft::member_selector(slices + numSlices, &pan_slice_t::len) , size_t(0)); char buffer[2048]; . . . // make sure to truncate the statement to a max 2047 characters \endcode * But this would unnecessarily constrain users of the Pantheios logging * functionality. * * 3. Finally, we could synthesise the functionality of auto_buffer * manually, as in: \code int pantheios_log_n( pan_sev_t severity , size_t numSlices , pan_slice_t const* slices) { // Calculate the total size of the log statement, by summation of the slice array const size_t n = std::accumulate(stlsoft::member_selector(slices, &pan_slice_t::len) , stlsoft::member_selector(slices + numSlices, &pan_slice_t::len) , size_t(0)); char buff[2048]; char *buffer = (n < 2048) ? &buff[0] : new char[1 + n]; . . . if(buffer != &buff[0]) { delete [] buffer; } \endcode * But this is onerous manual fiddling, and exception-unsafe. What would be * the point, when auto_buffer already does this (safely) for us? * * As a consequence of its blending of the best features of stack and heap * memory, auto_buffer is an invaluable component in the implementation of * many components within the STLSoft libraries, and in several other * open-source projects, including: * b64, * Open-RJ, * Pantheios, * recls, * and * shwild. * * \remarks auto_buffer works correctly whether the given allocator throws an * exception on allocation failure, or returns NULL. In the * latter case, construction failure to allocate is reflected by the size() * method returning 0. * * \remarks The design of auto_buffer is described in Chapter 32 of * Imperfect C++, and its * interface is discussed in detail in Section 16.2 of * Extended STL, volume 1. * * \note With version 1.9 of STLSoft, the order of the space and allocator * arguments were reversed. Further, the allocator default changed from * stlsoft::new_allocator to std::allocator for translators that support * the standard library. If you need the old characteristics, you can * \#define the symbol STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS. */ #if defined(STLSOFT_COMPILER_IS_MSVC) && \ _MSC_VER < 1200 # define STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS #endif /* compiler */ #if defined(STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS) # ifdef STLSOFT_AUTO_BUFFER_NEW_FORM # undef STLSOFT_AUTO_BUFFER_NEW_FORM # endif /* STLSOFT_AUTO_BUFFER_NEW_FORM */ // ////////////////////////////////////////////// // This is the pre-1.9 template parameter list template< ss_typename_param_k T # ifdef STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT , ss_typename_param_k A = ss_typename_type_def_k allocator_selector::allocator_type # else /* ? STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT */ , ss_typename_param_k A # endif /* STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT */ # ifdef STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT # if defined(STLSOFT_COMPILER_IS_BORLAND) , ss_size_t space = 256 # elif defined(STLSOFT_COMPILER_IS_DMC) , ss_size_t SPACE = 256 # else /* ? compiler */ , ss_size_t SPACE = auto_buffer_internal_size_calculator::value # endif /* compiler */ # else /* ? STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT */ # if !defined(STLSOFT_COMPILER_IS_BORLAND) , ss_size_t SPACE /* = auto_buffer_internal_size_calculator::value */ # else /* ? compiler */ , ss_size_t space /* = 256 */ # endif /* compiler */ # endif /* STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT */ > // End of pre-1.9 template parameter list // ////////////////////////////////////////////// #else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ // ////////////////////////////////////////////// // This is the 1.9+ template parameter list # ifndef STLSOFT_AUTO_BUFFER_NEW_FORM # define STLSOFT_AUTO_BUFFER_NEW_FORM # endif /* !STLSOFT_AUTO_BUFFER_NEW_FORM */ template< ss_typename_param_k T # ifdef STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT # if defined(STLSOFT_COMPILER_IS_BORLAND) , ss_size_t space = 256 # elif defined(STLSOFT_COMPILER_IS_DMC) , ss_size_t SPACE = 256 # else /* ? compiler */ , ss_size_t SPACE = auto_buffer_internal_size_calculator::value # endif /* compiler */ # else /* ? STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT */ # if !defined(STLSOFT_COMPILER_IS_BORLAND) , ss_size_t SPACE /* = auto_buffer_internal_size_calculator::value */ # else /* ? compiler */ , ss_size_t space /* = 256 */ # endif /* compiler */ # endif /* STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT */ # ifdef STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT , ss_typename_param_k A = ss_typename_type_def_k allocator_selector::allocator_type # else /* ? STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT */ , ss_typename_param_k A # endif /* STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT */ > // End of 1.9+ template parameter list // ////////////////////////////////////////////// #endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ class auto_buffer #if !defined(STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE) : protected A , public stl_collection_tag #else /* ? STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE */ : public stl_collection_tag #endif /* !STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE */ { /// \name Member Types /// @{ public: /// The value type typedef T value_type; /// The allocator type typedef A allocator_type; #if !defined(STLSOFT_COMPILER_IS_BORLAND) enum { /// The number of items in the internal buffer space = int(SPACE) // int() required for 64-bit compatibility }; #endif /* compiler */ /// The type of the current parameterisation #ifdef STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS typedef auto_buffer class_type; #else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ typedef auto_buffer class_type; #endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ /// The reference type typedef ss_typename_type_k allocator_type::reference reference; /// The non-mutable (const) reference type typedef ss_typename_type_k allocator_type::const_reference const_reference; /// The pointer type typedef ss_typename_type_k allocator_type::pointer pointer; /// The non-mutable (const) pointer type typedef ss_typename_type_k allocator_type::const_pointer const_pointer; /// The size type typedef ss_size_t size_type; /// The difference type typedef ss_ptrdiff_t difference_type; #if !defined(STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT) /// The iterator type typedef value_type* iterator; /// The non-mutable (const) iterator type typedef value_type const* const_iterator; #else /* ? !STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */ /// The iterator type typedef # if !defined(STLSOFT_COMPILER_IS_BORLAND) ss_typename_type_k # endif /* compiler */ pointer_iterator < value_type , pointer , reference >::type iterator; /// The non-mutating (const) iterator type typedef # if !defined(STLSOFT_COMPILER_IS_BORLAND) ss_typename_type_k # endif /* compiler */ pointer_iterator < value_type const , const_pointer , const_reference >::type const_iterator; /// The mutating (non-const) reverse iterator type typedef reverse_iterator_base < iterator , value_type , reference , pointer , difference_type > reverse_iterator; /// The non-mutating (const) reverse iterator type typedef const_reverse_iterator_base < const_iterator , value_type const , const_reference , const_pointer , difference_type > const_reverse_iterator; #endif /* !STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */ /// @} /// \name Implementation /// @{ private: pointer allocate_(size_type cItems, void const* hint) { #ifdef STLSOFT_LF_ALLOCATOR_ALLOCATE_HAS_HINT # ifdef STLSOFT_CF_STD_LIBRARY_IS_SUNPRO_RW return static_cast(get_allocator().allocate(cItems, const_cast(hint))); # else /* ? STLSOFT_CF_STD_LIBRARY_IS_SUNPRO_RW */ return get_allocator().allocate(cItems, hint); # endif /* STLSOFT_CF_STD_LIBRARY_IS_SUNPRO_RW */ #else /* ? STLSOFT_LF_ALLOCATOR_ALLOCATE_HAS_HINT */ STLSOFT_SUPPRESS_UNUSED(hint); return get_allocator().allocate(cItems); #endif /* STLSOFT_LF_ALLOCATOR_ALLOCATE_HAS_HINT */ } pointer allocate_(size_type cItems) { return allocate_(cItems, NULL); } 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(&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. If 0, the /// external array (if allocated) will be deallocated. /// \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(&m_internal[0]), m_buffer, cItems); deallocate_(m_buffer, m_cItems); m_buffer = const_cast(&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()); // Swap: // // 1. Allocator // 2. Member variables // 1. Allocator #if !defined(STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE) std_swap(static_cast(*this), static_cast(rhs)); #endif /* !STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE */ // 2. Member variables 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; } /// \brief Returns a reference to the last element in the buffer /// /// \pre The buffer instance must not be empty reference front() { STLSOFT_ASSERT(is_valid()); STLSOFT_MESSAGE_ASSERT("Cannot call front() on an empty buffer!", !empty()); return m_buffer[0]; } /// \brief Returns a reference to the last element in the buffer /// /// \pre The buffer instance must not be empty reference back() { STLSOFT_ASSERT(is_valid()); STLSOFT_MESSAGE_ASSERT("Cannot call back() on an empty buffer!", !empty()); return m_buffer[size() - 1]; } /// \brief Returns a non-mutating (const) reference to the last element /// in the buffer /// /// \pre The buffer instance must not be empty const_reference front() const { STLSOFT_ASSERT(is_valid()); STLSOFT_MESSAGE_ASSERT("Cannot call front() on an empty buffer!", !empty()); return m_buffer[0]; } /// \brief Returns a non-mutating (const) reference to the last element /// in the buffer /// /// \pre The buffer instance must not be empty const_reference back() const { STLSOFT_ASSERT(is_valid()); STLSOFT_MESSAGE_ASSERT("Cannot call back() on an empty buffer!", !empty()); return m_buffer[size() - 1]; } /// @} /// \name Iteration /// @{ public: /// \brief Returns a non-mutating iterator representing the start of the sequence const_iterator begin() const { STLSOFT_ASSERT(is_valid()); return m_buffer; } /// \brief Returns a non-mutating iterator representing the end of the sequence /// /// \note In the case where memory allocation has failed in the context /// where exceptions are not thrown for allocation failure, this method will /// return the same value as begin(). Hence, operations on the empty /// auto_buffer<> instance will be safe if made in respect of the range /// defined by [begin(), end()). const_iterator end() const { STLSOFT_ASSERT(is_valid()); return m_buffer + m_cItems; } /// \brief Returns a mutable iterator representing the start of the sequence iterator begin() { STLSOFT_ASSERT(is_valid()); return m_buffer; } /// \brief Returns a mutable iterator representing the end of the sequence /// /// \note In the case where memory allocation has failed in the context /// where exceptions are not thrown for allocation failure, this method will /// return the same value as begin(). Hence, operations on the empty /// auto_buffer<> instance will be safe if made in respect of the range /// defined by [begin(), end()). iterator end() { STLSOFT_ASSERT(is_valid()); return m_buffer + m_cItems; } #if defined(STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT) /// Begins the reverse iteration /// /// \return An iterator representing the start of the reverse sequence const_reverse_iterator rbegin() const { STLSOFT_ASSERT(is_valid()); return const_reverse_iterator(end()); } /// Ends the reverse iteration /// /// \return An iterator representing the end of the reverse sequence const_reverse_iterator rend() const { STLSOFT_ASSERT(is_valid()); return const_reverse_iterator(begin()); } /// Begins the reverse iteration /// /// \return An iterator representing the start of the reverse sequence reverse_iterator rbegin() { STLSOFT_ASSERT(is_valid()); return reverse_iterator(end()); } /// Ends the reverse iteration /// /// \return An iterator representing the end of the reverse sequence reverse_iterator rend() { STLSOFT_ASSERT(is_valid()); return reverse_iterator(begin()); } #endif /* STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */ /// @} /// \name Attributes /// @{ public: /// \brief Returns the number of elements in the auto_buffer /// /// \note In the case where memory allocation has failed in the context /// where exceptions are not thrown for allocation failure in the /// constructor, this method will return 0. Hence, operations on the /// empty auto_buffer<> instance will be safe if made in respect of /// the value returned by this method. size_type size() const { STLSOFT_ASSERT(is_valid()); return m_cItems; } /// \brief Returns the number of elements in the auto_buffer's internal buffer static size_type internal_size() { return space; } /// \brief Indicates whether the buffer has any contents /// /// \note This will only ever be true when an allocation above the number /// of elements in the internal array has been requested, and failed. ss_bool_t empty() const { STLSOFT_ASSERT(is_valid()); return 0 == m_cItems; } #if defined(STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVEx) /// \brief Returns an instance of the allocator used to specialise the /// instance. static allocator_type &get_allocator() { # if !defined(STLSOFT_STRICT) && \ defined(STLSOFT_COMPILER_IS_MSVC) && \ _MSC_VER >= 1310 # pragma warning(push) # pragma warning(disable : 4640) /* "construction of local static object is not thread-safe" - since it is here! (As long as one uses a 'conformant' allocator) - maybe use a spin_mutex in future */ # endif /* compiler */ static allocator_type s_allocator; return s_allocator; # if !defined(STLSOFT_STRICT) && \ defined(STLSOFT_COMPILER_IS_MSVC) && \ _MSC_VER >= 1310 # pragma warning(pop) # endif /* compiler */ } #else /* ? STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE */ /// \brief Returns an instance of the allocator used to specialise the /// instance. allocator_type get_allocator() const { # if defined(STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE) return allocator_type(); # else /* ? STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE */ return *this; # endif /* STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE */ } #endif /* STLSOFT_CF_ALLOCATOR_BASE_EXPENSIVE */ /// @} /// \name Implementation /// @{ private: ss_bool_t is_valid() const { ss_bool_t bRet = true; #if defined(STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK) if( space < m_cItems && !m_bExternal) { # ifdef STLSOFT_UNITTEST printf("auto_buffer is in external domain, but think's it isn't\n"); # endif /* STLSOFT_UNITTEST */ bRet = false; } if( !(space < m_cItems) && m_bExternal) { # ifdef STLSOFT_UNITTEST printf("auto_buffer is in internal domain, but think's it isn't\n"); # endif /* STLSOFT_UNITTEST */ bRet = false; } #else /* ? STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK */ if( space < m_cItems && !m_bExternal) { # ifdef STLSOFT_UNITTEST printf("auto_buffer is in external domain, but think's it isn't\n"); # endif /* STLSOFT_UNITTEST */ bRet = false; } #endif /* STLSOFT_AUTO_BUFFER_AGGRESSIVE_SHRINK */ if(m_bExternal) { if(m_buffer == &m_internal[0]) { #ifdef STLSOFT_UNITTEST printf("auto_buffer is in external domain, but buffer refers to internal array\n"); #endif /* STLSOFT_UNITTEST */ bRet = false; } } else { if(m_buffer != &m_internal[0]) { #ifdef STLSOFT_UNITTEST printf("auto_buffer is in internal domain, but buffer does not refer to internal array\n"); #endif /* STLSOFT_UNITTEST */ bRet = false; } } return bRet; } /// @} /// \name Members /// @{ private: pointer m_buffer; // Pointer to used buffer size_type m_cItems; // Number of items in buffer ss_bool_t m_bExternal; // This is required, since not allowed to compare m_buffer with &m_internal[0] - can't remember why; // NOTE: Check std value_type m_internal[space]; // Internal storage /// @} // Not to be implemented private: auto_buffer(class_type const& rhs); auto_buffer const& operator =(class_type const& rhs); }; #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION template< ss_typename_param_k T # ifdef STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT , ss_typename_param_k A = ss_typename_type_def_k allocator_selector::allocator_type # else /* ? STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT */ , ss_typename_param_k A # endif /* STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_CLASS_ARGUMENT_SUPPORT */ # ifdef STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT # if !defined(STLSOFT_COMPILER_IS_BORLAND) && \ !defined(STLSOFT_COMPILER_IS_DMC) , ss_size_t SPACE = auto_buffer_internal_size_calculator::value # else /* ? compiler */ , ss_size_t SPACE = 256 # endif /* compiler */ # else /* ? STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT */ , ss_size_t SPACE /* = auto_buffer_internal_size_calculator::value */ # endif /* STLSOFT_CF_TEMPLATE_CLASS_DEFAULT_FUNDAMENTAL_ARGUMENT_SUPPORT */ > class auto_buffer_old # if defined(STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS) : public auto_buffer # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ : public auto_buffer # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ { /// \name Member Types /// @{ private: # if defined(STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS) typedef auto_buffer parent_class_type; # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ typedef auto_buffer parent_class_type; # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ typedef auto_buffer_old class_type; public: typedef ss_typename_type_k parent_class_type::value_type value_type; typedef ss_typename_type_k parent_class_type::allocator_type allocator_type; typedef ss_typename_type_k parent_class_type::reference reference; typedef ss_typename_type_k parent_class_type::const_reference const_reference; typedef ss_typename_type_k parent_class_type::pointer pointer; typedef ss_typename_type_k parent_class_type::const_pointer const_pointer; typedef ss_typename_type_k parent_class_type::size_type size_type; typedef ss_typename_type_k parent_class_type::difference_type difference_type; typedef ss_typename_type_k parent_class_type::iterator iterator; typedef ss_typename_type_k parent_class_type::const_iterator const_iterator; # if defined(STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT) typedef ss_typename_type_k parent_class_type::reverse_iterator reverse_iterator; typedef ss_typename_type_k parent_class_type::const_reverse_iterator const_reverse_iterator; # endif /* STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */ /// @} /// \name Construction /// @{ public: ss_explicit_k auto_buffer_old(size_type cItems) : parent_class_type(cItems) {} /// @} // Not to be implemented private: auto_buffer_old(class_type const& rhs); class_type& operator =(class_type const& rhs); }; #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /* ///////////////////////////////////////////////////////////////////////// * swapping */ #if !defined(STLSOFT_COMPILER_IS_WATCOM) template< ss_typename_param_k T # ifdef STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS , ss_typename_param_k A , ss_size_t SPACE # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ , ss_size_t SPACE , ss_typename_param_k A # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ > # ifdef STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS inline void swap(auto_buffer& lhs, auto_buffer& rhs) # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ inline void swap(auto_buffer& lhs, auto_buffer& rhs) # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ { lhs.swap(rhs); } #endif /* compiler */ /* ///////////////////////////////////////////////////////////////////////// * Shims */ #ifndef STLSOFT_CF_TEMPLATE_SHIMS_NOT_SUPPORTED template< ss_typename_param_k T # ifdef STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS , ss_typename_param_k A , ss_size_t SPACE # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ , ss_size_t SPACE , ss_typename_param_k A # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ > # ifdef STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS inline ss_bool_t is_empty(auto_buffer const& b) # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ inline ss_bool_t is_empty(auto_buffer const& b) # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ { return b.empty(); } #endif /* !STLSOFT_CF_TEMPLATE_SHIMS_NOT_SUPPORTED */ //////////////////////////////////////////////////////////////////////////// // Unit-testing #ifdef STLSOFT_UNITTEST # include "./unittest/auto_buffer_unittest_.h" #endif /* STLSOFT_UNITTEST */ /* ////////////////////////////////////////////////////////////////////// */ #ifndef _STLSOFT_NO_NAMESPACE } // namespace stlsoft #endif /* _STLSOFT_NO_NAMESPACE */ /* In the special case of Intel behaving as VC++ 7.0 or earlier on Win32, we * illegally insert into the std namespace. */ #if defined(STLSOFT_CF_std_NAMESPACE) # if ( ( defined(STLSOFT_COMPILER_IS_INTEL) && \ defined(_MSC_VER))) && \ _MSC_VER < 1310 namespace std { template< ss_typename_param_k T # ifdef STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS , ss_typename_param_k A , stlsoft_ns_qual(ss_size_t) SPACE # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ , stlsoft_ns_qual(ss_size_t) SPACE , ss_typename_param_k A # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ > # ifdef STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS inline void swap(stlsoft_ns_qual(auto_buffer)& lhs, stlsoft_ns_qual(auto_buffer)& rhs) # else /* ? STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ inline void swap(stlsoft_ns_qual(auto_buffer)& lhs, stlsoft_ns_qual(auto_buffer)& rhs) # endif /* STLSOFT_AUTO_BUFFER_USE_PRE_1_9_CHARACTERISTICS */ { lhs.swap(rhs); } } // namespace std # endif /* INTEL && _MSC_VER < 1310 */ #endif /* STLSOFT_CF_std_NAMESPACE */ /* ////////////////////////////////////////////////////////////////////// */ #endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER */ /* ///////////////////////////// end of file //////////////////////////// */