/*--------------------------------------------------------------------------*\

    FILE....: FIFO.CPP
    TYPE....: C++ Functions
    AUTHOR..: David Rowe
    DATE....: 19/11/97
    AUTHOR..: Ron Lee
    DATE....: 5/3/07

    Functions used to implement First In First Out (FIFO) queues of 16 bit
    words.


         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1999-2007 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License as published by the Free Software Foundation; either
         version 2.1 of the License, or (at your option) any later version.

         This library 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
         Lesser General Public License for more details.

         You should have received a copy of the GNU Lesser General Public
         License along with this library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include <cstring>
#include "fifo.h"


HostFifo::HostFifo(size_t size)
    : m_start( new uint16_t[size] )
    , m_end( m_start + size )
    , m_wr( m_start )
    , m_rd( m_start )
    , m_size( size )
{ //{{{
	pthread_mutex_init(&m_mutex,NULL);
} //}}}

HostFifo::~HostFifo()
{ //{{{
	delete [] m_start;
	pthread_mutex_destroy(&m_mutex);
} //}}}

Fifo::Status HostFifo::Write(uint16_t *buf, size_t len)
{ //{{{
	pthread_mutex_lock(&m_mutex);

	// determine if there is enough room for buf 
	size_t words_free = (m_rd > m_wr) ? m_rd - m_wr - 1
					  : m_size - (m_wr - m_rd) - 1;

	if(words_free < len) {
		pthread_mutex_unlock(&m_mutex);
		return Fifo::FULL;
	}

	// If buf overlaps end of linear array split into two block moves 
	if((m_wr + len) > m_end) {
		size_t copy_first = m_end - m_wr;

		memcpy(m_wr, buf, copy_first * sizeof(uint16_t));
		memcpy(m_start, buf + copy_first, (len - copy_first) * sizeof(uint16_t));
	} else  memcpy(m_wr, buf, len * sizeof(uint16_t));

	// increment pwr and wrap around if required 
	uint16_t *new_pwr = m_wr + len;
	m_wr = (new_pwr < m_end) ? new_pwr : m_start + (new_pwr - m_end);

	pthread_mutex_unlock(&m_mutex);

	return Fifo::OK;
} //}}}

Fifo::Status HostFifo::Read(uint16_t *buf, size_t len)
{ //{{{
	pthread_mutex_lock(&m_mutex);

	// determine if there is enough data to fill buf 
	size_t words_used = (m_rd > m_wr) ? m_size - (m_rd - m_wr)
					  : m_wr - m_rd;
	if(words_used < len) {
		pthread_mutex_unlock(&m_mutex);
		return Fifo::EMPTY;
	}

	// If buf overlaps end of linear array split into two block moves 
	if((m_rd + len) > m_end) {
		size_t copy_first = m_end - m_rd;

		memcpy(buf, m_rd, copy_first * sizeof(uint16_t));
		memcpy(buf + copy_first, m_start, (len - copy_first) * sizeof(uint16_t));
	} else  memcpy(buf, m_rd, len * sizeof(uint16_t));

	// increment prd and wrap around if required
	uint16_t *new_prd = m_rd + len;
	m_rd = (new_prd < m_end) ? new_prd : m_start + (new_prd - m_end);

	pthread_mutex_unlock(&m_mutex);

	return Fifo::OK;
} //}}}

size_t HostFifo::HowFull()
{ //{{{
	pthread_mutex_lock(&m_mutex);

	size_t words = (m_rd > m_wr) ? m_size - (m_rd - m_wr) : m_wr - m_rd;

	pthread_mutex_unlock(&m_mutex);
	return words;
} //}}}

size_t HostFifo::HowEmpty()
{ //{{{
	return m_size - 1 - HowFull();
} //}}}

void HostFifo::Flush()
{ //{{{
	pthread_mutex_lock(&m_mutex);
	m_wr = m_rd = m_start;
	pthread_mutex_unlock(&m_mutex);
} //}}}

