/**
* @file CM_Queue.c
* @brief 队列函数实现
* @details
* 1.本队列适用的应用方式有(单任务应用），（两个并行任务），（一个应用+一个中断）。
* 2 本队列支持的两任务或者中断函数必须是一读一写，不支持两个任务同时读或者两个任务同时写
* 3 申请足够空间防止队列写满。
* 4 请不要输入 0 qsize。
* 5 如果多任务或者中断情况下，如果空间小于两帧数据的容量，会发生死锁：数据长度不够读，剩余空间又不够写，发生死锁
* 6 本队列最大支持4G的数据，但是使用者应根据平台情况酌情申请空间。
* @author GT-BU
* @date 2021.02.01
* @version V1.1
* @copyright TCT
* @par 修改日志
* <table>
* <tr><th>Date        <th>Version  <th>Author     <th>Description
* <tr><td>2017.12.20  <td>V1.0     <td>MSCP项目组 <td>创建初始版本
* <tr><td>2021.02.01  <td>V1.1     <td>GT-BU      <td>TSCP项目集成、更新
* </table>
*/

#include "CM_Queue.h"
#include "CM_Memory.h"

/**
* @brief 队列初始化
* @param pQueue 队列指针
* @param Qsize 队列需要的长度
* @return 执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* 用于初始化一个队列，与CM_QueueElementFree配对使用
* 为队列结构体中的pQ分配空间，并将队列的size初始化
*/
ENUM_CM_RETURN CM_QueueInitial(INOUT STRU_CM_QUEUE_T *pQueue, IN CM_UINT32 QSize)
{
    /*- 变量初始化 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;

    /*- 输入值是否合法 */
    if ((NULL != pQueue) && (0u != QSize))
    {
        /*- 队列初始化为空 */
        pQueue->in = 0u;
        pQueue->out = 0u;
        pQueue->qsize = QSize;
        /*- 按照指定大小分配内存 */
        pQueue->pQ = (CM_QueueElement*)CM_Malloc((CM_SIZE_T)(sizeof(CM_QueueElement) * pQueue->qsize));
        /*- 内存分配是否成功 */
        if (pQueue->pQ != NULL)
        {
            /*- 函数执行结果置为成功 */
            rt = ENUM_CM_TRUE;
        }
        else
		{
			/*- 无处理 */
		}
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回函数执行结果 */
    return rt;
}

/**
* @brief 队列初始化，使用传入的静态空间
* @param pQueue 队列指针
* @param Qsize 队列需要的长度
* @param pQueueElementSpace 队列需要的内存空间
* @return 执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* 1.确认pQueueElementSpace数组的大小大于等于QSize，以免异常。
* 2.如果使用这个函数初始化，则不再调用CM_QueueElementFree进行空间释放。
*/
ENUM_CM_RETURN CM_StaticQueueInitial(INOUT STRU_CM_QUEUE_T *pQueue, IN CM_UINT32 QSize, IN CM_QueueElement pQueueElementSpace[])
{
    /*- 变量初始化 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;

    /*- 输入参数是否合法 */
    if ((NULL != pQueue) && (0u != QSize))
    {
        /*- 队列初始化为空 */
        pQueue->in = 0u;
        pQueue->out = 0u;
        pQueue->qsize = QSize;
        /*- 使用输入的内存空间分配内存 */
        pQueue->pQ = pQueueElementSpace;     /*%RELAX<MISRA_17_6>*/
        rt = ENUM_CM_TRUE;
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回函数执行结果 */
    return rt;
}

/*
* 功能描述： 队列扫描函数，
* 参数说明： CM_UINT32 DataLen,              要读出的数据长度;
*            CM_QueueElement *pData,         读出的数据要存放的数组;
*            STRU_CM_QUEUE_T *pQueue,         要读出数据的队列
* 返回值：   0x55，成功
*            0xAA，失败，队列数据长度不够
*/

/**
* @brief 队列扫描函数
* @param DataLen 要读出的数据长度
* @param pData 读出的数据要存放的数组
* @param pQueue 要读出数据的队列
* @return 执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* 可以读出队列中的数据，但是对队列的读写指针都不产生影响，即读到的数据还在队列中
*/
ENUM_CM_RETURN CM_QueueScan(IN CM_UINT32 DataLen, OUT CM_QueueElement *pData, IN const STRU_CM_QUEUE_T *pQueue)
{
    /*- 变量初始化 */
    register CM_UINT32 i = 0u; /*临时变量*/
    register CM_UINT32 j = 0u;/*临时变量*/
    register CM_QueueElement *pOut = NULL;/*临时变量*/
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;/*函数返回值*/

    /*- 队列中的数据是否不够读 */
    if ((CM_QueueStatus(pQueue) < DataLen) || (DataLen == 0u))
    {
        /*- 函数执行结果置为失败 */
        rt = ENUM_CM_FALSE;
    }
    else
    {
        /*到达此处必然能够保证读到必须的数据*/
        /*- 确定读指针位置 */
        pOut = &(pQueue->pQ[pQueue->out]);
        j = pQueue->qsize - (pQueue->out);

        /*- 读指针后面是否有足够多的数据（不需要翻转）*/
        if (j >= DataLen)
        {
            /*- @alias 不翻转读完数据 */
            for (i = 0u; i < DataLen; i++)
            {
                *pData = *pOut;
                pData++;   /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pOut++;    /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }
        }
        else
        {
            /*不翻转不能读完数据*/

            /*- @alias 读到队列尾*/
            for (i = 0u; i < j; i++)
            {
                *pData = *pOut;
                pData++;   /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pOut++;    /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }

            /*- 将读指针指向数组头*/
            pOut = &pQueue->pQ[0];

            /*- @alias 从队列头开始读剩下的数*/
            for (; i < DataLen; i++)
            {
                *pData = *pOut;
                pData++;  /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pOut++;   /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }
        }
        /*- 函数执行结果置为成功 */
        rt = ENUM_CM_TRUE;
    }
    /*- 返回函数执行结果 */
    return rt;
}


/**
* @brief 读队列函数
* @param DataLen 要读出的数据长度
* @param pData 读出的数据要存放的数组
* @param pQueue 要读出数据的队列
* @return 执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* 可以读出队列中的数据,并存放在一个数组中，数据读出后读指针发生变化，即读出的数据已不存在队列中
*/
ENUM_CM_RETURN CM_QueueRead(IN CM_UINT32 DataLen, OUT CM_QueueElement *pData, INOUT STRU_CM_QUEUE_T *pQueue)
{
    /*- 变量初始化 */
    register CM_UINT32 i = 0u; /* 临时变量 */
    register CM_UINT32 j = 0u;/* 临时变量 */
    register CM_QueueElement *pOut = NULL;/* 临时变量 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;/* 函数返回值 */

    /*- 队列中的数据是否不够读 */
    if ((CM_QueueStatus(pQueue) < DataLen) || (DataLen == 0u))
    {
        /*- 函数返回值置为失败 */
        rt = ENUM_CM_FALSE;
    }
    else
    {
        /*到达此处必然能够保证读到必须的数据*/
        /*- 确定读指针位置 */
        pOut = &(pQueue->pQ[pQueue->out]);
        j = pQueue->qsize - (pQueue->out);

        /*- 读指针后面是否有足够多的数据（不需要翻转）*/
        if (j > DataLen)
        {
            /*- @alias 不翻转读完数据 */
            for (i = 0u; i < DataLen; i++)
            {
                *pData = *pOut;
                pData++;  /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pOut++;   /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }
            /*- 更新读指针 */
            pQueue->out += DataLen;
        }
        else
        {
            /*j<=DataLen 不翻转不能读完数据*/

            /*- @alias 读到队列尾*/
            for (i = 0u; i < j; i++)
            {
                *pData = *pOut;
                pData++;  /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pOut++;   /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }

            /*- 将读指针指向数组头*/
            pOut = &pQueue->pQ[0];

            /*- @alias 从队列头开始读剩下的数*/
            for (; i < DataLen; i++)
            {
                *pData = *pOut;
                pData++;      /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pOut++;       /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }
            /*- 更新读指针 */
            pQueue->out = DataLen - j;
        }
        /*- 函数执行结果置为成功 */
        rt = ENUM_CM_TRUE;
    }
    /*- 返回函数执行结果 */
    return rt; /* 返回 */
}

/**
* @brief 清空队列函数
* @param pQueue 需要清空的队列指针
* @return 执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* 把队列中的in，out值置0
*/
ENUM_CM_RETURN CM_QueueClear(INOUT STRU_CM_QUEUE_T *pQueue)
{
    /*- 复位读、写指针*/
    pQueue->in = 0u;
    pQueue->out = 0u;
    /*- 返回成功 */
    return    ENUM_CM_TRUE;
}

/**
* @brief 读出队列中可用数据长度
* @param pQueue 要读出的数据长度的队列
* @return 执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* 返回队列中可用的数据长度，如果队列为空，返回0
*/
CM_UINT32 CM_QueueStatus(IN const STRU_CM_QUEUE_T *pQueue)
{
    /*- 变量初始化 */
    CM_UINT32 qStatus = 0u;
    /*- 队列是否已经初始化 */
    if (pQueue->qsize != 0u)
    {
        /*- 计算数据长度 */
        qStatus = ((pQueue->in + pQueue->qsize) - pQueue->out) % pQueue->qsize;
    }
    else
    {
        /*- 数据长度置0 */
        qStatus = 0u;
    }
    /*- 返回数据长度 */
    return qStatus;
}

/**
* @brief 写队列函数
* @param DataLen 要写入的数据长度
* @param pData 要写入的数据存放的数组
* @param pQueue 要写入数据的队列
* @return 执行结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
* @details
* 将一个数组中的数据写到一个队列中
*/
ENUM_CM_RETURN CM_QueueWrite(IN CM_UINT32 DataLen, IN const CM_QueueElement *pData, INOUT STRU_CM_QUEUE_T *pQueue)
{
    /*- 变量初始化 */
    register CM_UINT32 i = 0u; /* 临时变量 */
    register CM_UINT32 j = 0u;/* 临时变量 */
    register CM_QueueElement *pIn = NULL;/* 临时变量 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE; /* 函数返回值 */

    /*先判断队列是否有能够的空间容纳写入的数据,如果队列要满了，就不允许写了 */
    /*- 队列剩余空间是否够写 */
    if ((pQueue == NULL) || (pData == NULL) || (DataLen >= (pQueue->qsize - CM_QueueStatus(pQueue))))
    {
        /*- 函数返回值置为失败 */
        rt = ENUM_CM_FALSE;
    }
    else
    {

        /*由于已经能够判定有足够的空间，不需要再对out值进行判断*/
        /*- 确定写指针位置 */
        j = pQueue->qsize - (pQueue->in);
        pIn = &(pQueue->pQ[pQueue->in]);

        /*- 读指针后面是否有足够大的空间（不需要翻转）*/
        if (j > DataLen)
        {
            /*写入时可以不用翻转*/

            /*用自加指令可以明显提高指令的执行速度*/
            /*- @alias 不翻转写完数据 */
            for (i = 0u; i < DataLen; i++)
            {
                *pIn = *pData;
                pIn++;      /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pData++;    /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }

            /*- 更新写指针 */
            pQueue->in += DataLen;
        }
        else /*j <= DataLen*/
        {
            /*写入过程中必然翻转*/

            /*将数据从当前指针到缓冲区尾部，写入的长度为j*/
            /*- @alias 写到数组末端 */
            for (i = 0u; i < j; i++)
            {
                *pIn = *pData;
                pIn++;    /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pData++;  /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }

            /*已经写到缓冲区尾部了，应该从缓冲区头开始写起*/
            /*- 更新写指针到数组头部 */
            pIn = &pQueue->pQ[0];

            /*从队列头开始写，i的初始值为j——即为上次写到缓冲区尾的长度 */
            /*- @alias 从数组头开始写剩余的数据 */
            for (; i < DataLen; i++)
            {
                *pIn = *pData;
                pIn++;    /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
                pData++;  /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            }

            /*调整队列的in指针为写入的总长度减去写到缓冲区尾的数据长度*/
            /*- 更新写指针 */
            pQueue->in = DataLen - j;
        }
        /*- 函数执行结果置为成功 */
        rt = ENUM_CM_TRUE;
    }
    /*- 返回函数执行结果 */
    return rt;
}


/**
* @brief 队列存储空间释放函数
* @param pQueue 队列
* @details
* 在不需要某个队列的时候，把队列的元素数组空间释放掉,与CM_QueueInitial配对使用
*/
void CM_QueueElementFree(INOUT STRU_CM_QUEUE_T *pQueue)
{
    /*- 队列指针归位*/
    pQueue->in = 0u;
    pQueue->out = 0u;
    pQueue->qsize = 0u;

    /*- @alias 释放队列空间*/
    if (pQueue->pQ != NULL)
    {
        free((void*)pQueue->pQ);
        pQueue->pQ = NULL;
    }
    else
	{
		/*- 无处理 */
	}
}

/**
* @brief 在队列中删除一定的元素
* @param Length 要删除的元素个数
* @param pQueue 队列
*/
void CM_QueueElementDiscard(IN CM_UINT16 Length, INOUT STRU_CM_QUEUE_T *pQueue)
{
	/*- 变量初始化 */
	CM_UINT32 qLen = 0u;
    /*- 队列是否已经初始化 */
	if(pQueue->qsize > 0u)
	{
		/*- 获取队列长度 */
		qLen = CM_QueueStatus(pQueue);
		/*- 要删除的元素个数是否大于队列中的有效数据总量 */
		if (qLen < Length)
		{
			/*- 清空队列 */
			(void)CM_QueueClear(pQueue);
		}
		else
		{
			/*- 移动读指针删除指定数量的队列元素 */
			pQueue->out = (pQueue->out + Length) % pQueue->qsize;
		}
	}
	else
	{
		/*- 无处理 */
	}
}

/**
* @brief 读出队列中可用空间大小
* @param pQueue 队列
* @return 可用空间大小
* @details 由于队列不能写满，队列中有一个空间不能用
*/
CM_UINT32 CM_QueueGetSpace(IN const STRU_CM_QUEUE_T *pQueue)
{
    /*- 变量初始化 */
    CM_UINT32 qSpace = 0u;
    /*- 队列是否已经初始化 */
    if (pQueue->qsize != 0u)
    {
        /*- 计算剩余可用空间大小 */
        qSpace = ((pQueue->out + pQueue->qsize) - pQueue->in - 1u) % pQueue->qsize;
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回可用空间大小 */
    return qSpace;
}

