mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-07 06:44:18 +00:00
491 lines
16 KiB
C
491 lines
16 KiB
C
|
/*
|
||
|
Copyright (c) 2012, Broadcom Europe Ltd
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
* Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
* Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
* Neither the name of the copyright holder nor the
|
||
|
names of its contributors may be used to endorse or promote products
|
||
|
derived from this software without specific prior written permission.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include "containers/core/containers_bits.h"
|
||
|
#include "containers/core/containers_common.h"
|
||
|
|
||
|
#ifdef ENABLE_CONTAINERS_LOG_FORMAT
|
||
|
#include "containers/core/containers_logging.h"
|
||
|
#endif
|
||
|
|
||
|
/******************************************************************************
|
||
|
Defines and constants.
|
||
|
******************************************************************************/
|
||
|
|
||
|
#ifdef ENABLE_CONTAINERS_LOG_FORMAT
|
||
|
/** String used for indentation. If more spaces are needed, just add them. */
|
||
|
#define INDENT_SPACES_STRING "> "
|
||
|
#define INDENT_SPACES_LENGTH (sizeof(INDENT_SPACES_STRING) - 1)
|
||
|
#endif /* ENABLE_CONTAINERS_LOG_FORMAT */
|
||
|
|
||
|
/******************************************************************************
|
||
|
Type definitions
|
||
|
******************************************************************************/
|
||
|
|
||
|
/******************************************************************************
|
||
|
Function prototypes
|
||
|
******************************************************************************/
|
||
|
|
||
|
/******************************************************************************
|
||
|
Local Functions
|
||
|
******************************************************************************/
|
||
|
|
||
|
#ifdef ENABLE_CONTAINERS_LOG_FORMAT
|
||
|
|
||
|
/**************************************************************************//**
|
||
|
* Returns a string that indicates whether the bit stream is valid or not.
|
||
|
*
|
||
|
* \pre bit_stream is not NULL.
|
||
|
*
|
||
|
* \param bit_stream The bit stream object.
|
||
|
* \return A string indicating the validity of the stream.
|
||
|
*/
|
||
|
static const char * vc_container_bits_valid_str( VC_CONTAINER_BITS_T *bit_stream )
|
||
|
{
|
||
|
return vc_container_bits_valid(bit_stream) ? "" : " - stream invalid";
|
||
|
}
|
||
|
|
||
|
/**************************************************************************//**
|
||
|
* Returns a string of spaces the length of which is determined by the
|
||
|
* parameter.
|
||
|
* The length is limited to a certain size, above which a greater than symbol
|
||
|
* prefixes the maximum number of spaces.
|
||
|
*
|
||
|
* \param length The required length of the string.
|
||
|
* \return A string indicating the validity of the stream.
|
||
|
*/
|
||
|
static const char * vc_container_bits_indent_str(uint32_t length)
|
||
|
{
|
||
|
uint32_t str_length = length;
|
||
|
|
||
|
if (str_length > INDENT_SPACES_LENGTH)
|
||
|
str_length = INDENT_SPACES_LENGTH;
|
||
|
|
||
|
return INDENT_SPACES_STRING + (INDENT_SPACES_LENGTH - str_length);
|
||
|
}
|
||
|
|
||
|
#endif /* ENABLE_CONTAINERS_LOG_FORMAT */
|
||
|
|
||
|
/**************************************************************************//**
|
||
|
* Returns the number of consecutive zero bits in the stream.
|
||
|
* the zero bits are terminated either by a one bit, or the end of the stream.
|
||
|
* In the former case, the zero bits and the terminating one bit are removed
|
||
|
* from the stream.
|
||
|
* In the latter case, the stream becomes invalid. The stream also becomes
|
||
|
* invalid if there are not as many bits after the one bit as zero bits before
|
||
|
* it.
|
||
|
* If the stream is already or becomes invalid, zero is returned.
|
||
|
*
|
||
|
* \pre bit_stream is not NULL.
|
||
|
*
|
||
|
* \param bit_stream The bit stream object.
|
||
|
* \return The number of consecutive zero bits, or zero if the stream is
|
||
|
* invalid.
|
||
|
*/
|
||
|
static uint32_t vc_container_bits_get_leading_zero_bits( VC_CONTAINER_BITS_T *bit_stream )
|
||
|
{
|
||
|
uint32_t leading_zero_bits;
|
||
|
uint32_t bits_left = vc_container_bits_available(bit_stream);
|
||
|
uint32_t bits;
|
||
|
uint8_t mask, current_byte;
|
||
|
|
||
|
if (!bits_left)
|
||
|
return vc_container_bits_invalidate(bit_stream);
|
||
|
|
||
|
/* Cache 'bits' field to avoid repeated pointer access */
|
||
|
bits = bit_stream->bits;
|
||
|
if (bits)
|
||
|
{
|
||
|
current_byte = *bit_stream->buffer;
|
||
|
mask = 1 << (bits - 1);
|
||
|
} else {
|
||
|
/* Initialize variables to placate the compiler */
|
||
|
current_byte = 0;
|
||
|
mask = 0;
|
||
|
}
|
||
|
|
||
|
/* Scan for the first one bit, counting the number of zeroes. This gives the
|
||
|
* number of further bits after the one that are part of the value. See
|
||
|
* section 9.1 of ITU-T REC H.264 201003 for more details. */
|
||
|
|
||
|
for (leading_zero_bits = 0; leading_zero_bits < bits_left; leading_zero_bits++)
|
||
|
{
|
||
|
if (!bits)
|
||
|
{
|
||
|
if (!bit_stream->bytes)
|
||
|
return vc_container_bits_invalidate(bit_stream);
|
||
|
bit_stream->bytes--;
|
||
|
current_byte = *(++bit_stream->buffer);
|
||
|
bits = 8;
|
||
|
mask = 0x80;
|
||
|
}
|
||
|
|
||
|
bits--;
|
||
|
bits_left--;
|
||
|
if (current_byte & mask)
|
||
|
break; /* Found the marker bit */
|
||
|
|
||
|
mask >>= 1;
|
||
|
}
|
||
|
|
||
|
/* Check enough bits are left in the stream for the value. */
|
||
|
if (leading_zero_bits > bits_left)
|
||
|
return vc_container_bits_invalidate(bit_stream);
|
||
|
|
||
|
/* Return cached value of bits to the stream */
|
||
|
bit_stream->bits = bits;
|
||
|
|
||
|
return leading_zero_bits;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
Functions exported as part of the bit stream API
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream,
|
||
|
const uint8_t *buffer,
|
||
|
uint32_t available)
|
||
|
{
|
||
|
vc_container_assert(buffer && (buffer != (const uint8_t *)1));
|
||
|
|
||
|
/* Start with buffer pointing at the previous byte with no bits available
|
||
|
* to make the mathematics easier */
|
||
|
bit_stream->buffer = buffer - 1;
|
||
|
bit_stream->bytes = available;
|
||
|
bit_stream->bits = 0;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream )
|
||
|
{
|
||
|
bit_stream->buffer = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
return (bit_stream->buffer != NULL);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
bit_stream->bytes = 0;
|
||
|
bit_stream->bits = 0;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
const uint8_t *buffer = bit_stream->buffer;
|
||
|
|
||
|
/* Only valid on byte boundaries, where buffer pointer has not been moved yet */
|
||
|
vc_container_assert(!bit_stream->bits);
|
||
|
|
||
|
return buffer ? (buffer + 1) : NULL;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst,
|
||
|
const VC_CONTAINER_BITS_T *src)
|
||
|
{
|
||
|
memcpy(dst, src, sizeof(VC_CONTAINER_BITS_T));
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
if (!bit_stream->buffer)
|
||
|
return 0;
|
||
|
return (bit_stream->bytes << 3) + bit_stream->bits;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
if (!bit_stream->buffer)
|
||
|
return 0;
|
||
|
|
||
|
vc_container_assert(!bit_stream->bits);
|
||
|
|
||
|
return vc_container_bits_available(bit_stream) >> 3;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream,
|
||
|
uint32_t bits_to_skip)
|
||
|
{
|
||
|
uint32_t have_bits;
|
||
|
uint32_t new_bytes;
|
||
|
|
||
|
have_bits = vc_container_bits_available(bit_stream);
|
||
|
if (have_bits < bits_to_skip)
|
||
|
{
|
||
|
vc_container_bits_invalidate(bit_stream);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
have_bits -= bits_to_skip;
|
||
|
new_bytes = have_bits >> 3;
|
||
|
bit_stream->bits = have_bits & 7;
|
||
|
bit_stream->buffer += (bit_stream->bytes - new_bytes);
|
||
|
bit_stream->bytes = new_bytes;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream,
|
||
|
uint32_t bytes_to_skip)
|
||
|
{
|
||
|
/* Only valid on byte boundaries */
|
||
|
vc_container_assert(!bit_stream->bits);
|
||
|
|
||
|
vc_container_bits_skip(bit_stream, bytes_to_skip << 3);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream,
|
||
|
uint32_t bytes_to_reduce)
|
||
|
{
|
||
|
if (bit_stream->bytes >= bytes_to_reduce)
|
||
|
bit_stream->bytes -= bytes_to_reduce;
|
||
|
else
|
||
|
vc_container_bits_invalidate(bit_stream);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream,
|
||
|
uint32_t bytes_to_copy,
|
||
|
uint8_t *dst)
|
||
|
{
|
||
|
vc_container_assert(!bit_stream->bits);
|
||
|
|
||
|
if (bit_stream->bytes < bytes_to_copy)
|
||
|
{
|
||
|
/* Not enough data */
|
||
|
vc_container_bits_invalidate(bit_stream);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* When the number of bits is zero, the next byte to take is at buffer + 1 */
|
||
|
memcpy(dst, bit_stream->buffer + 1, bytes_to_copy);
|
||
|
bit_stream->buffer += bytes_to_copy;
|
||
|
bit_stream->bytes -= bytes_to_copy;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream,
|
||
|
uint32_t value_bits)
|
||
|
{
|
||
|
uint32_t value = 0;
|
||
|
uint32_t needed = value_bits;
|
||
|
uint32_t bits;
|
||
|
|
||
|
vc_container_assert(value_bits <= 32);
|
||
|
|
||
|
if (needed > vc_container_bits_available(bit_stream))
|
||
|
return vc_container_bits_invalidate(bit_stream);
|
||
|
|
||
|
bits = bit_stream->bits;
|
||
|
while (needed)
|
||
|
{
|
||
|
uint32_t take;
|
||
|
|
||
|
if (!bits)
|
||
|
{
|
||
|
bit_stream->bytes--;
|
||
|
bit_stream->buffer++;
|
||
|
bits = 8;
|
||
|
}
|
||
|
|
||
|
take = bits;
|
||
|
if (needed < take) take = needed;
|
||
|
|
||
|
bits -= take;
|
||
|
needed -= take;
|
||
|
|
||
|
value <<= take;
|
||
|
if (take == 8)
|
||
|
value |= *bit_stream->buffer; /* optimize whole byte case */
|
||
|
else
|
||
|
value |= (*bit_stream->buffer >> bits) & ((1 << take) - 1);
|
||
|
}
|
||
|
|
||
|
bit_stream->bits = bits;
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
vc_container_bits_skip(bit_stream, vc_container_bits_get_leading_zero_bits(bit_stream));
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
uint32_t leading_zero_bits;
|
||
|
uint32_t codeNum;
|
||
|
|
||
|
leading_zero_bits = vc_container_bits_get_leading_zero_bits(bit_stream);
|
||
|
|
||
|
/* Anything bigger than 32 bits is definitely overflow */
|
||
|
if (leading_zero_bits > 32)
|
||
|
return vc_container_bits_invalidate(bit_stream);
|
||
|
|
||
|
codeNum = vc_container_bits_read_u32(bit_stream, leading_zero_bits);
|
||
|
|
||
|
if (leading_zero_bits == 32)
|
||
|
{
|
||
|
/* If codeNum is non-zero, it would need 33 bits, so is also overflow */
|
||
|
if (codeNum)
|
||
|
return vc_container_bits_invalidate(bit_stream);
|
||
|
|
||
|
return 0xFFFFFFFF;
|
||
|
}
|
||
|
|
||
|
return codeNum + (1 << leading_zero_bits) - 1;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream)
|
||
|
{
|
||
|
uint32_t uval;
|
||
|
|
||
|
uval = vc_container_bits_read_u32_exp_golomb(bit_stream);
|
||
|
|
||
|
/* The signed Exp-Golomb code 0xFFFFFFFF cannot be represented as a signed 32-bit
|
||
|
* integer, because it should be one larger than the largest positive value. */
|
||
|
if (uval == 0xFFFFFFFF)
|
||
|
return vc_container_bits_invalidate(bit_stream);
|
||
|
|
||
|
/* Definition of conversion is
|
||
|
* s = ((-1)^(u + 1)) * Ceil(u / 2)
|
||
|
* where '^' is power, but this should be equivalent */
|
||
|
return ((int32_t)((uval & 1) << 1) - 1) * (int32_t)((uval >> 1) + (uval & 1));
|
||
|
}
|
||
|
|
||
|
#ifdef ENABLE_CONTAINERS_LOG_FORMAT
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
void vc_container_bits_log(VC_CONTAINER_T *p_ctx,
|
||
|
uint32_t indent,
|
||
|
const char *txt,
|
||
|
VC_CONTAINER_BITS_T *bit_stream,
|
||
|
VC_CONTAINER_BITS_LOG_OP_T op,
|
||
|
uint32_t length)
|
||
|
{
|
||
|
const char *valid_str = vc_container_bits_valid_str(bit_stream);
|
||
|
const char *indent_str = vc_container_bits_indent_str(indent);
|
||
|
|
||
|
switch (op)
|
||
|
{
|
||
|
case VC_CONTAINER_BITS_LOG_SKIP:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bits skipped%s", indent_str, txt, length, valid_str);
|
||
|
break;
|
||
|
case VC_CONTAINER_BITS_LOG_SKIP_BYTES:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes skipped%s", indent_str, txt, length, valid_str);
|
||
|
break;
|
||
|
case VC_CONTAINER_BITS_LOG_COPY_BYTES:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes copied%s", indent_str, txt, length, valid_str);
|
||
|
break;
|
||
|
case VC_CONTAINER_BITS_LOG_REDUCE_BYTES:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes reduced%s", indent_str, txt, length, valid_str);
|
||
|
break;
|
||
|
case VC_CONTAINER_BITS_LOG_EG_SKIP:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: Exp-Golomb value skipped%s", indent_str, txt, valid_str);
|
||
|
break;
|
||
|
default:
|
||
|
/* Unexpected operation. Check bit stream logging macros */
|
||
|
vc_container_assert(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx,
|
||
|
uint32_t indent,
|
||
|
const char *txt,
|
||
|
VC_CONTAINER_BITS_T *bit_stream,
|
||
|
VC_CONTAINER_BITS_LOG_OP_T op,
|
||
|
uint32_t length,
|
||
|
uint32_t value)
|
||
|
{
|
||
|
const char *valid_str = vc_container_bits_valid_str(bit_stream);
|
||
|
const char *indent_str = vc_container_bits_indent_str(indent);
|
||
|
|
||
|
switch (op)
|
||
|
{
|
||
|
case VC_CONTAINER_BITS_LOG_U8:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%02x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str);
|
||
|
break;
|
||
|
case VC_CONTAINER_BITS_LOG_U16:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%04x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str);
|
||
|
break;
|
||
|
case VC_CONTAINER_BITS_LOG_U32:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str);
|
||
|
break;
|
||
|
case VC_CONTAINER_BITS_LOG_EG_U32:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) unsigned Exp-Golomb%s", indent_str, txt, value, value, valid_str);
|
||
|
break;
|
||
|
default:
|
||
|
/* Unexpected operation. Check bit stream logging macros */
|
||
|
vc_container_assert(0);
|
||
|
}
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx,
|
||
|
uint32_t indent,
|
||
|
const char *txt,
|
||
|
VC_CONTAINER_BITS_T *bit_stream,
|
||
|
VC_CONTAINER_BITS_LOG_OP_T op,
|
||
|
uint32_t length,
|
||
|
int32_t value)
|
||
|
{
|
||
|
const char *valid_str = vc_container_bits_valid_str(bit_stream);
|
||
|
const char *indent_str = vc_container_bits_indent_str(indent);
|
||
|
|
||
|
VC_CONTAINER_PARAM_UNUSED(length);
|
||
|
|
||
|
switch (op)
|
||
|
{
|
||
|
case VC_CONTAINER_BITS_LOG_EG_S32:
|
||
|
vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%d) signed Exp-Golomb%s", indent_str, txt, value, value, valid_str);
|
||
|
break;
|
||
|
default:
|
||
|
/* Unexpected operation. Check bit stream logging macros */
|
||
|
vc_container_assert(0);
|
||
|
}
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
#endif /* ENABLE_CONTAINERS_LOG_FORMAT */
|