/*
 * Copyright (C) 2007-2008 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/**
 * 
 * File Name:  armCOMM_Bitstream.c
 * OpenMAX DL: v1.0.2
 * Revision:   9641
 * Date:       Thursday, February 7, 2008
 * 
 * 
 * 
 *
 * Defines bitstream encode and decode functions common to all codecs
 */

#include "omxtypes.h"
#include "armCOMM.h"
#include "armCOMM_Bitstream.h"

/***************************************
 * Fixed bit length Decode
 ***************************************/

/**
 * Function: armLookAheadBits()
 *
 * Description:
 * Get the next N bits from the bitstream without advancing the bitstream pointer
 *
 * Parameters:
 * [in]     **ppBitStream
 * [in]     *pOffset
 * [in]     N=1...32
 *
 * Returns  Value
 */

OMX_U32 armLookAheadBits(const OMX_U8 **ppBitStream, OMX_INT *pOffset, OMX_INT N)
{
    const OMX_U8 *pBitStream = *ppBitStream;
    OMX_INT Offset = *pOffset;
    OMX_U32 Value;

    armAssert(Offset>=0 && Offset<=7);
    armAssert(N>=1 && N<=32);

    /* Read next 32 bits from stream */
    Value = (pBitStream[0] << 24 ) | ( pBitStream[1] << 16)  | (pBitStream[2] << 8 ) | (pBitStream[3]) ;
    Value = (Value << Offset ) | (pBitStream[4] >> (8-Offset));

    /* Return N bits */
    return Value >> (32-N);
}


/**
 * Function: armGetBits()
 *
 * Description:
 * Read N bits from the bitstream
 *    
 * Parameters:
 * [in]     *ppBitStream
 * [in]     *pOffset
 * [in]     N=1..32
 *
 * [out]    *ppBitStream
 * [out]    *pOffset
 * Returns  Value
 */


OMX_U32 armGetBits(const OMX_U8 **ppBitStream, OMX_INT *pOffset, OMX_INT N)
{
    const OMX_U8 *pBitStream = *ppBitStream;
    OMX_INT Offset = *pOffset;
    OMX_U32 Value;
    
    if(N == 0)
    {
      return 0;
    }

    armAssert(Offset>=0 && Offset<=7);
    armAssert(N>=1 && N<=32);

    /* Read next 32 bits from stream */
    Value = (pBitStream[0] << 24 ) | ( pBitStream[1] << 16)  | (pBitStream[2] << 8 ) | (pBitStream[3]) ;
    Value = (Value << Offset ) | (pBitStream[4] >> (8-Offset));

    /* Advance bitstream pointer by N bits */
    Offset += N;
    *ppBitStream = pBitStream + (Offset>>3);
    *pOffset = Offset & 7;

    /* Return N bits */
    return Value >> (32-N);
}

/**
 * Function: armByteAlign()
 *
 * Description:
 * Align the pointer *ppBitStream to the next byte boundary
 *
 * Parameters:
 * [in]     *ppBitStream
 * [in]     *pOffset
 *
 * [out]    *ppBitStream
 * [out]    *pOffset
 *
 **/
 
OMXVoid armByteAlign(const OMX_U8 **ppBitStream,OMX_INT *pOffset)
{
    if(*pOffset > 0)
    {
        *ppBitStream += 1;
        *pOffset = 0;
    }    
}

/** 
 * Function: armSkipBits()
 *
 * Description:
 * Skip N bits from the value at *ppBitStream
 *
 * Parameters:
 * [in]     *ppBitStream
 * [in]     *pOffset
 * [in]     N
 *
 * [out]    *ppBitStream
 * [out]    *pOffset
 *
 **/


OMXVoid armSkipBits(const OMX_U8 **ppBitStream,OMX_INT *pOffset,OMX_INT N)
{
    OMX_INT Offset = *pOffset;
    const OMX_U8 *pBitStream = *ppBitStream;
   
    /* Advance bitstream pointer by N bits */
    Offset += N;
    *ppBitStream = pBitStream + (Offset>>3);
    *pOffset = Offset & 7;
}

/***************************************
 * Variable bit length Decode
 ***************************************/

/**
 * Function: armUnPackVLC32()
 *
 * Description:
 * Variable length decode of variable length symbol (max size 32 bits) read from
 * the bit stream pointed by *ppBitStream at *pOffset by using the table
 * pointed by pCodeBook
 * 
 * Parameters:
 * [in]     *pBitStream
 * [in]     *pOffset
 * [in]     pCodeBook
 * 
 * [out]    *pBitStream
 * [out]    *pOffset
 *
 * Returns : Code Book Index if successfull. 
 *         : ARM_NO_CODEBOOK_INDEX = -1 if search fails.
 **/
#ifndef C_OPTIMIZED_IMPLEMENTATION 

OMX_U16 armUnPackVLC32(
    const OMX_U8 **ppBitStream,
    OMX_INT *pOffset,
    const ARM_VLC32 *pCodeBook
)
{    
    const OMX_U8 *pBitStream = *ppBitStream;
    OMX_INT Offset = *pOffset;
    OMX_U32 Value;
    OMX_INT Index;
        
    armAssert(Offset>=0 && Offset<=7);

    /* Read next 32 bits from stream */
    Value = (pBitStream[0] << 24 ) | ( pBitStream[1] << 16)  | (pBitStream[2] << 8 ) | (pBitStream[3]) ;
    Value = (Value << Offset ) | (pBitStream[4] >> (8-Offset));

    /* Search through the codebook */    
    for (Index=0; pCodeBook->codeLen != 0; Index++)
    {
        if (pCodeBook->codeWord == (Value >> (32 - pCodeBook->codeLen)))
        {
            Offset       = Offset + pCodeBook->codeLen;
            *ppBitStream = pBitStream + (Offset >> 3) ;
            *pOffset     = Offset & 7;
            
            return Index;
        }        
        pCodeBook++;
    }

    /* No code match found */
    return ARM_NO_CODEBOOK_INDEX;
}

#endif

/***************************************
 * Fixed bit length Encode
 ***************************************/

/**
 * Function: armPackBits
 *
 * Description:
 * Pack a VLC code word into the bitstream
 *
 * Remarks:
 *
 * Parameters:
 * [in] ppBitStream     pointer to the pointer to the current byte 
 *                      in the bit stream.
 * [in] pOffset         pointer to the bit position in the byte 
 *                      pointed by *ppBitStream. Valid within 0
 *                      to 7.
 * [in] codeWord        Code word that need to be inserted in to the
 *                          bitstream
 * [in] codeLength      Length of the code word valid range 1...32
 *
 * [out] ppBitStream    *ppBitStream is updated after the block is encoded,
 *                          so that it points to the current byte in the bit
 *                          stream buffer.
 * [out] pBitOffset     *pBitOffset is updated so that it points to the
 *                          current bit position in the byte pointed by
 *                          *ppBitStream.
 *
 * Return Value:
 * Standard OMX_RESULT result. See enumeration for possible result codes.
 *
 */
 
OMXResult armPackBits (
    OMX_U8  **ppBitStream, 
    OMX_INT *pOffset,
    OMX_U32 codeWord, 
    OMX_INT codeLength 
)
{
    OMX_U8  *pBitStream = *ppBitStream;
    OMX_INT Offset = *pOffset;
    OMX_U32 Value;
        
    /* checking argument validity */
    armRetArgErrIf(Offset < 0, OMX_Sts_BadArgErr);
    armRetArgErrIf(Offset > 7, OMX_Sts_BadArgErr);
    armRetArgErrIf(codeLength < 1, OMX_Sts_BadArgErr);
    armRetArgErrIf(codeLength > 32, OMX_Sts_BadArgErr);

    /* Prepare the first byte */
    codeWord = codeWord << (32-codeLength);
    Value = (pBitStream[0] >> (8-Offset)) << (8-Offset);
    Value = Value | (codeWord >> (24+Offset));

    /* Write out whole bytes */
    while (8-Offset <= codeLength)
    {
        *pBitStream++ = (OMX_U8)Value;
        codeWord   = codeWord  << (8-Offset);
        codeLength = codeLength - (8-Offset);
        Offset = 0;
        Value = codeWord >> 24;
    }

    /* Write out final partial byte */
    *pBitStream  = (OMX_U8)Value;
    *ppBitStream = pBitStream;
    *pOffset = Offset + codeLength;
    
    return  OMX_Sts_NoErr;
}
 
/***************************************
 * Variable bit length Encode
 ***************************************/

/**
 * Function: armPackVLC32
 *
 * Description:
 * Pack a VLC code word into the bitstream
 *
 * Remarks:
 *
 * Parameters:
 * [in]	ppBitStream		pointer to the pointer to the current byte 
 *                      in the bit stream.
 * [in]	pBitOffset	    pointer to the bit position in the byte 
 *                      pointed by *ppBitStream. Valid within 0
 *                      to 7.
 * [in]	 code     		VLC code word that need to be inserted in to the
 *                      bitstream
 *
 * [out] ppBitStream	*ppBitStream is updated after the block is encoded,
 *	                    so that it points to the current byte in the bit
 *						stream buffer.
 * [out] pBitOffset		*pBitOffset is updated so that it points to the
 *						current bit position in the byte pointed by
 *						*ppBitStream.
 *
 * Return Value:
 * Standard OMX_RESULT result. See enumeration for possible result codes.
 *
 */
 
OMXResult armPackVLC32 (
    OMX_U8 **ppBitStream, 
    OMX_INT *pBitOffset,
    ARM_VLC32 code 
)
{
    return (armPackBits(ppBitStream, pBitOffset, code.codeWord, code.codeLen));
}

/*End of File*/
