/* substratequeue.cc
****************************************************************************************************************
****************************************************************************************************************

    Copyright (C) 2021 Askar Almatov

    This file is part of Trafficmeter.

    Trafficmeter 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, either version 3 of the License, or (at your
    option) any later version.

    Trafficmeter 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 Trafficmeter. If not, see
    <http://www.gnu.org/licenses/>.
*/

#include <cassert>

#include "substratequeue.h"
#include "tmdefs.h"

using namespace iclass;
using namespace trafficmeter;

constexpr const unsigned    ALLOCATION_SIZE_ = sizeof( Substrate ) + SUBSTRATE_SIZE;

/**************************************************************************************************************/
SubstrateQueue::SubstrateQueue( int nChunks ) :
    ring_( nChunks ),
    next_( nullptr ),
    size_a_( 0 )
{
    queue_c_ = new Substrates;
}

/**************************************************************************************************************/
SubstrateQueue::~SubstrateQueue()
{
    this->lock();
    assert( queue_c_->empty() );
    delete queue_c_;
}

/**************************************************************************************************************/
void
SubstrateQueue::tieTo( SubstrateQueue* head )
{
    if ( head == nullptr )
    {
        next_ = this;
        return;
    }

    SubstrateQueue*     last = head;

    while ( last->next_ != head )
    {
        last = last->next_;
    }

    last->next_ = this;
    next_ = head;
}

/**************************************************************************************************************/
SubstrateQueue*
SubstrateQueue::next()
{
    return next_;
}

/**************************************************************************************************************/
Substrate*
SubstrateQueue::newSubstrate()
{
    return new( RingAllocator::allocate(&ring_, ALLOCATION_SIZE_) ) Substrate;
}

/**************************************************************************************************************/
void
SubstrateQueue::push( Substrate* substrate )
{
    this->lock();
    queue_c_->push_back( substrate );
    ++size_a_;
    this->unlock();
}

/**************************************************************************************************************/
Substrates*
SubstrateQueue::pull()
{
    Substrates*     substrates = nullptr;

    if ( size_a_ > 0 )
    {
        this->lock();
        substrates = queue_c_;
        queue_c_ = new Substrates;
        size_a_ -= substrates->size();
        this->unlock();
    }

    return substrates;
}

/**************************************************************************************************************/
long
SubstrateQueue::size() const
{
    return size_a_;
}
