//////////////////////////////////////////////////////////////////////////////// /** \file \brief \ru Очередь объектов, которые не имеют деструкторов. \en Queue of objects without destructors. \~ */ //////////////////////////////////////////////////////////////////////////////// #ifndef __TEMPL_S_QUEUE_H #define __TEMPL_S_QUEUE_H #include #include #include #include //------------------------------------------------------------------------------ /** \brief \ru Очередь объектов, которые не имеют деструкторов. \en Queue of objects without destructors. \~ \details \ru Очередь объектов, которые не имеют деструкторов. \n Требования к объектам очереди такие же, как в SArray. \en Queue of objects without destructors. \n Requirement for the objects are the same as in SArray. \~ \attention \ru В целях эффективности контейнера проверки обращения к запредельной памяти реализованы только под откладкой \en In order to efficiency of the container validation - the usage of prohibitive memory are implemented only for debug version \~ \par \ru Рекомендации по использованию Если предполагается фиксированный (известный) наибольший размер очереди, то рекомендуется использовать функцию SQueue::Push() для добавления объекта. Если предельный размер очереди не известен, то следует применять метод SQueue::PushAlloc(), который при добавлении "наращивает" память по мере необходимости. \en Usage recommendations If the fixed (known) maximum size of queue is assumed then it is recommended to use the function SQueue::Push() to add an object. If the limit size of queue is unknown then there should be applied the method SQueue::PushAlloc() which builds up the memory as necessary. \~ \par \ru Правило выделения памяти Если размера буфера для хранения объектов очереди не достаточно, то выделяется новый участок кучи с размеров на SQueue::delta больше. Если SQueue::delta = 1, то размер выделенной памяти уведичивается на 12,5% при каждом достижении предела. \en The rule of memory allocation If the size of the buffer for storage of queue objects is not enough then allocated a new part of the heap which has size greater on SQueue::delta. If SQueue::delta = 1, then the size of allocated memory is increased by 12,5% each time the limit is reached. \~ \ingroup Base_Tools_Containers */ // --- template class SQueue { Type * data; ///< \ru Адрес начала выделенной памяти. \en An address of the beginning of the allocated memory. Type * qlast; ///< \ru Адрес конца выделенной памяти (последний элемент массива выделенной памяти). \en An address of the end of the allocated memory (the last element of the allocated memory's array) Type * qp1; ///< \ru Самый первый извлекаемый (queue pointer 1). \en The first extracted one (queue pointer 1). Type * qp2; ///< \ru Следующий элемент от последнего вошедшего (queue pointer 2). \en The next element from the last entering (queue pointer 2). public: /// \ru Конструктор. \en Constructor. SQueue( size_t capacity = 0 ); /// \ru Деструктор. \en Destructor. virtual ~SQueue(); public: /// \ru Выдать размер выделенной памяти. \en Get the size of the allocated memory. size_t Capacity() const { return qlast+1-data; } /// \ru Последний элемент очереди. \en The last element of the queue. Type & Back(); /// \ru Последний элемент очереди. \en The last element of the queue. const Type & Back() const; /// \ru Первый элемент очереди. \en The first element of the queue. Type & Front(); /// \ru Первый элемент очереди. \en The first element of the queue. const Type & Front() const; /// \ru Свойство пустого множества. \en A property of the empty set. bool Empty() const { return qp1 == qp2; } /// \ru Свойство исчерпанной памяти. \en An expended memory property. bool IsFull() const; /// \ru Самый первый, выходящий из очереди (корректно работает только для непустой очереди). \en The very first one outgoing from the queue (it works correctly only for nonempty queue). Type & First() const; /// \ru Добавить в очередь и нарастить буфер выделенной памяти при необходимости. \en Add to the queue and increase the buffer of the allocated memory if it is necessary. void Push( const Type & obj ); /// \ru Вывести из очереди. \en Move out from queue. void Pop(); /// \ru Вывести из очереди. \en Move out from queue. void Pop( Type & obj ); /// \ru Зарезервировать дополнительную память. \en Reserve an additional memory. bool Reserve( size_t ); /// \ru Очистить очередь. \en Clear the queue. void SetEmpty() { qp1 = qp2 = data; } /// \ru Выдать размер очереди. \en Get the queue size. size_t Size() const; private: /// \ru Инкремент указателя с учётом зацикленности памяти. \en An increment of the pointer considering a looped memory. Type * _IncPtr( Type * ptr ) const; /// \ru Добавить в очередь без проверки израсходованной памяти. \en Add to the queue without check of consumed memory. void _Push( const Type & u ); /// \ru Задать наибольший размер очереди. \en Set the maximum size of the queue. bool _NewCapacity( size_t max_len, bool clear ); }; //------------------------------------------------------------------------------- // // --- template SQueue::SQueue( size_t capacity ) : data( nullptr ) , qlast( nullptr ) , qp1( nullptr ) , qp2( nullptr ) { if ( capacity > 0 ) { try { data = new Type[capacity]; } catch ( const std::bad_alloc & ) { data = nullptr; throw; } } qlast = data+capacity-1; qp1 = qp2 = data; } //------------------------------------------------------------------------------- // // --- template SQueue::~SQueue() { delete [] data; #ifdef C3D_DEBUG ::memset( &qp1, 0xFE, sizeof(Type *) ); ::memset( &qp2, 0xFE, sizeof(Type *) ); ::memset( &qlast, 0xFE, sizeof(Type *) ); ::memset( &data, 0xFE, sizeof(Type *) ); #endif // C3D_DEBUG } //------------------------------------------------------------------------------- /// \ru Кто крайний? (корректно работает только для непустой очереди); \en Which is the last? (it works correctly only for nonempty queue); // --- template inline const Type & SQueue::Back() const { PRECONDITION( !Empty() && Capacity() > 0 ); return qp2 == data ? *qlast : *( qp2 - 1 ); } //------------------------------------------------------------------------------- /// \ru Кто крайний? (корректно работает только для непустой очереди) \en Which is the last? (it works correctly only for nonempty queue) // --- template inline Type & SQueue::Back() { PRECONDITION( !Empty() && Capacity() > 0 ); return qp2 == data ? *qlast : *( qp2 - 1 ); } //------------------------------------------------------------------------------- // \ru Первый элемент очереди. \en The first element of the queue. // --- template inline Type & SQueue::Front() { PRECONDITION( Capacity() > 0 && !Empty() ); return *qp1; } //------------------------------------------------------------------------------- // \ru Первый элемент очереди. \en The first element of the queue. // --- template inline const Type & SQueue::Front() const { PRECONDITION( Capacity() > 0 && !Empty() ); return *qp1; } //------------------------------------------------------------------------------- // \ru Самый первый, выходящий из очереди (корректно работает только для непустой очереди). \en The very first one outgoing from the queue (it works correctly only for nonempty queue). // --- template inline Type & SQueue::First() const { PRECONDITION( qp1 <= qlast && qp1 >= data && qp1 != qp2 ); return *qp1; } //------------------------------------------------------------------------------- /// \ru Свойство полностью исчерпанного буфера \en A expended buffer property. /**\ru Фактически свойство отвечает возможно ли добавить в очередь элемент без передислокации буфера. \en In fact this property answeres whether it is possible to add in a queue an element without redeployment of the buffer. \~ */ // --- template inline bool SQueue::IsFull() const { return data == nullptr || _IncPtr( qp2 ) == qp1; } //------------------------------------------------------------------------------- /// \ru Инкремент указателя с учётом зацикленности памяти \en An increment of the pointer considering a looped memory //--- template inline Type * SQueue::_IncPtr( Type * ptr ) const { PRECONDITION( ptr >= data && ptr <= qlast ); if ( ++ptr > qlast ) ptr = data; return ptr; } //------------------------------------------------------------------------------- // // --- template inline void SQueue::_Push( const Type & obj ) { PRECONDITION( !IsFull() && Size() < Capacity() ); if ( !IsFull() && Size() < Capacity() ) { memcpy( qp2, &obj, sizeof( Type ) ); qp2 = _IncPtr( qp2 ); PRECONDITION( !Empty() ); // \ru после добавления очередь не может быть пустой \en the queue may bacome empty after the adding PRECONDITION( qp2 >= data && qp2 <= qlast ); } } //------------------------------------------------------------------------------- /// \ru Добавить в очередь и нарастить буфер выделенной памяти при необходимости \en Add to the queue and increase the buffer of the llocated memory if it is necessary /**\ru Тоже, что и #SQueue::Push, но с перезахватом памяти при исчерпании буфера \en The same as #SQueue::Push, but with memory reallocation in a case when the buffer is full \~ */ // --- template void SQueue::Push( const Type & obj ) { if ( IsFull() ) // \ru Требуется перезахват памяти \en A memory reallocation is required { size_t cap = Capacity(); cap += ::KsAutoDelta( cap ); if ( !_NewCapacity(cap, false/*clear*/) ) return; } _Push( obj ); PRECONDITION( qp2 >= data && qp2 <= qlast ); } //------------------------------------------------------------------------------- // // --- template inline void SQueue::Pop( Type & obj ) { PRECONDITION( qp2 != qp1 ); // \ru перед извлечением очередь не может быть пустой \en the queue can't be empty before the extraction if ( qp2 != qp1 ) { memcpy( &obj, qp1, sizeof( Type ) ); qp1 = _IncPtr( qp1 ); PRECONDITION( qp1 <= qlast && qp1 >= data ); } } //------------------------------------------------------------------------------- // // --- template inline void SQueue::Pop() { PRECONDITION( qp2 != qp1 ); // \ru перед извлечением очередь не может быть пустой \en the queue can't be empty before the extraction if ( qp2 != qp1 ) { qp1 = _IncPtr( qp1 ); PRECONDITION( qp1 <= qlast && qp1 >= data ); } } //------------------------------------------------------------------------------- /// \ru Зарезервировать дополнительную память \en Reserve an additional memory // --- template bool SQueue::Reserve( size_t addCapacity ) { PRECONDITION( addCapacity > 0 ); const size_t futureSize = Size() + addCapacity; // \ru Предполагаемый размер, растущей очереди \en An assumed size of increasing queue if ( Capacity() < futureSize ) { return _NewCapacity( futureSize, false ); } return true; } //------------------------------------------------------------------------------- /// \ru Выдать размер очереди \en Get the queue size //--- template inline size_t SQueue::Size() const { if ( qp2 >= qp1 ) { return qp2 - qp1; // \ru Нефрагментированная очередь \en Not fragmented queue } return ( qlast + 1 - qp1 ) + ( qp2 - data ); // \ru Сумма длин двух фрагментов очереди \en The lengths sum of two fragments of queue } //------------------------------------------------------------------------------- /// \ru Задать наибольший размер очереди \en Set the maximum size of the queue // --- template bool SQueue::_NewCapacity( size_t max_len, bool clear ) { if ( clear || Empty() || max_len == 0 ) { try { delete [] data; data = qlast = qp1 = qp2 = nullptr; if ( max_len > 0 ) { data = new Type[max_len]; qlast = data + max_len - 1; qp1 = qp2 = data; } } catch ( ... ) { data = qlast = qp1 = qp2 = nullptr; C3D_CONTROLED_THROW; return false; } } else { PRECONDITION( qp1 != qp2 && max_len>0 && clear == false ); // \ru Выражение обязано быть истинным \en The expression should be true Type * n_data = nullptr; try { n_data = new Type[max_len]; if ( qp1 < qp2 ) // \ru Вариант без фрагментации \en A variant without fragmentation { size_t len = std_min( (size_t)/*OV_x64 (uint)*/( qp2 - qp1 ), max_len ); // \ru Количество ячеек с полезной информацией (не байты) \en The number of cells with useful information (not bites) PRECONDITION( len <= max_len ); ::memcpy( n_data, qp1, len * sizeof( Type ) ); delete[] data; data = qp1 = qp2 = n_data; qp2 += len; } else if ( qp1 > qp2 ) // \ru Вариант с фрагментированным массивом полезной информации \en A variant with the fragmented array of useful information { size_t len1 = std_min( (size_t)( qlast - qp1 + 1 ), max_len ); // \ru Количество ячеек первого фрагмента \en The number of cells of the first fragment size_t len2 = std_min( (size_t)( qp2 - data ), (size_t)( max_len - len1 ) ); // \ru Количество ячеек второго фрагмента \en The number of cells of the second fragment PRECONDITION( len1 + len2 <= max_len ); ::memcpy( n_data, qp1, len1 * sizeof( Type ) ); ::memcpy( n_data + len1, data, len2 * sizeof( Type ) ); delete[] data; data = qp1 = qp2 = n_data; qp2 += len1; qp2 += len2; } qlast = data + max_len - 1; } catch ( ... ) { C3D_CONTROLED_THROW; return false; } } return true; } #endif // __TEMPL_S_QUEUE_H