mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
490 lines
16 KiB
490 lines
16 KiB
/* |
|
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 */
|
|
|