/*****************************************************************************
* bits.c test bitstream reader
*****************************************************************************
* Copyright (C) 2015 VLC authors and VideoLAN
*
* This program 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 program 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 program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "../../libvlc/test.h"
#include <vlc_bits.h>
#include "../../../modules/packetizer/hxxx_ep3b.h"
#define test_assert(foo, bar) do {\
ssize_t a = (foo); \
ssize_t b = (bar); \
if(a != b) { \
printf("Failed (%s) line %d : %" PRId64 " != %" PRId64 "\n", psz_tag, __LINE__, (int64_t)(a), (int64_t)(b) ); \
return 1; \
} } while( 0 )
#define TESTSET_COUNT 4
struct testset
{
uint8_t count;
const uint8_t data[12];
};
enum dataset
{
TESTSET0 = 0,
TESTSET1,
TESTSET_EXPGOLOMB,
TESTSET2,
} ;
#define bs_init(a,b,c) \
if( callbacks ) { bs_init_custom( a, b, c, callbacks, cb_priv ); \
if( cb_priv ) { priv_init( cb_priv ); }; } \
else { bs_init( a, b, c); }
static int run_tests( const struct testset *p_testsets,
const char *psz_tag,
bs_byte_callbacks_t *callbacks,
void *cb_priv, void (*priv_init)(void *) )
{
bs_t bs;
bs_init( &bs, NULL, 0 );
test_assert( bs_pos(&bs), 0 );
test_assert( bs_eof(&bs), true );
test_assert( bs_error(&bs), false );
bs_init( &bs, p_testsets[TESTSET0].data, 0 );
test_assert( bs_pos(&bs), 0 );
bs_skip( &bs, 3 );
test_assert( bs_error(&bs), true );
bs_init( &bs, p_testsets[TESTSET0].data,
p_testsets[TESTSET0].count );
test_assert( bs_pos(&bs), 0 );
test_assert( bs_eof(&bs), false );
test_assert( bs_error(&bs), false );
bs_skip( &bs, 3 );
test_assert( bs_error(&bs), false );
test_assert( bs_pos(&bs), 3 );
bs_init( &bs, p_testsets[TESTSET1].data,
p_testsets[TESTSET1].count );
test_assert( bs_error(&bs), false );
bs_write( &bs, 1, 0 );
test_assert( bs_error(&bs), false );
test_assert( bs_eof(&bs), false );
bs_read1( &bs );
test_assert( bs_error(&bs), false );
test_assert( bs_eof(&bs), false );
test_assert( bs_pos(&bs), 1 );
bs_read( &bs, 7 );
test_assert( bs_pos(&bs), 8 );
bs_read1( &bs );
test_assert( bs_pos(&bs), 9 );
test_assert( bs_error(&bs), false );
bs_align( &bs );
test_assert( bs_pos(&bs), 16 );
test_assert( bs_error(&bs), false );
bs_init( &bs, p_testsets[TESTSET1].data,
p_testsets[TESTSET1].count );
test_assert( bs_read(&bs, 4), 0x0A );
test_assert( bs_read(&bs, 12), ((0x0A << 8) | 0x55) );
bs_init( &bs, p_testsets[TESTSET_EXPGOLOMB].data,
p_testsets[TESTSET_EXPGOLOMB].count );
test_assert( bs_read_ue(&bs), 0x09 );
test_assert( bs_error(&bs), false );
test_assert( bs_read1(&bs), 1 );
test_assert( bs_read_se(&bs), 2 );
test_assert( bs_error(&bs), false );
test_assert( bs_read_se(&bs), -1 );
test_assert( bs_eof(&bs), !0 );
bs_init( &bs, p_testsets[TESTSET2].data,
p_testsets[TESTSET2].count );
bs_skip( &bs, 24 );
test_assert( bs_read( &bs, 8 ), 0xDD );
test_assert( bs_read( &bs, 4 ), 0x0E );
test_assert( bs_read( &bs, 8 ), 0xEF );
test_assert( bs_error(&bs), false );
bs_init( &bs, p_testsets[TESTSET2].data,
p_testsets[TESTSET2].count );
bs_skip( &bs, 40 );
test_assert( bs_read( &bs, 8 ), 0xFF );
bs_init( &bs, p_testsets[TESTSET2].data,
p_testsets[TESTSET2].count );
bs_skip( &bs, 20 );
test_assert( bs_read( &bs, 8 ), 0xCD );
test_assert( bs_read( &bs, 4 ), 0x0D );
test_assert( bs_read( &bs, 8 ), 0xEE );
test_assert( bs_error(&bs), false );
test_assert( bs_eof(&bs), false );
if( !callbacks )
{
bs_init( &bs, p_testsets[TESTSET2].data, p_testsets[TESTSET2].count );
bs_skip( &bs, p_testsets[TESTSET2].count * 8 );
test_assert( bs_eof(&bs), true );
test_assert( bs_error(&bs), false );
bs_init( &bs, p_testsets[TESTSET2].data, p_testsets[TESTSET2].count );
bs_skip( &bs, p_testsets[TESTSET2].count * 8 + 1 );
test_assert( bs_error(&bs), true );
test_assert( bs_eof(&bs), true );
}
/* */
bs_init( &bs, p_testsets[TESTSET2].data,
p_testsets[TESTSET2].count );
for( size_t i=0; i<6*8; i++ )
{
test_assert(bs_aligned( &bs ), !!(i%8 == 0));
test_assert( bs_eof(&bs), false );
test_assert(bs_pos( &bs ), i);
bs_read( &bs, 1 );
test_assert( bs_error(&bs), false );
}
test_assert(bs_eof( &bs ), 1);
/* test writes */
uint8_t buf[5] = { 0 };
const uint8_t bufok[5] = { 0x7D, 0xF7, 0xDF, 0x7D, 0xF7 };
bs_write_init( &bs, &buf, 5 );
bs_write(&bs, 1, 1 );
test_assert(buf[0], 0x80);
bs_write(&bs, 2, 0 );
test_assert(buf[0], 0x80);
bs_write(&bs, 1, 1 );
test_assert(buf[0], 0x90);
bs_write_init( &bs, &buf, 5 );
for( size_t i=0, j=0; i<5*8; j++ )
{
test_assert(bs_aligned( &bs ), !!(i%8 == 0));
test_assert( bs_eof(&bs), false );
test_assert(bs_pos( &bs ), i);
bs_write(&bs, j % 4, (i % 2) ? 0xFF >> (8 - (j % 4)) : 0 );
i += j % 4;
test_assert( bs_error(&bs), i > 5*8 );
}
test_assert(bs_eof( &bs ), 1);
test_assert(!memcmp(buf, bufok, 5), true);
bs_write_init( &bs, &buf, 5 );
bs_write( &bs, 1, 0 );
bs_write_align( &bs, 1 );
test_assert(bs_aligned( &bs ), true);
test_assert(bs_pos( &bs ), 8);
test_assert(buf[0], 0x7F);
bs_write( &bs, 1, 1 );
bs_write_align( &bs, 0 );
test_assert(bs_aligned( &bs ), true);
test_assert(bs_pos( &bs ), 16);
test_assert(buf[1], 0x80);
/* overflows */
bs_init( &bs, p_testsets[TESTSET1].data, p_testsets[TESTSET1].count );
bs_read( &bs, 42 );
test_assert( bs_error(&bs), true );
test_assert( bs_eof(&bs), true );
test_assert(bs_pos( &bs ), 16);
bs_init( &bs, p_testsets[TESTSET1].data, p_testsets[TESTSET1].count );
bs_skip( &bs, 2 );
test_assert( bs_error(&bs), false );
test_assert( bs_eof(&bs), false );
bs_skip( &bs, 40 );
test_assert( bs_error(&bs), true );
test_assert( bs_eof(&bs), true );
test_assert(bs_pos( &bs ), 16);
bs_init( &bs, p_testsets[TESTSET1].data, p_testsets[TESTSET1].count );
bs_skip( &bs, 8 );
test_assert( bs_error(&bs), false );
test_assert(bs_pos( &bs ), 8);
test_assert(bs_read( &bs, 8 + 2 ), 0x55 << 2); /* truncated read */
test_assert( bs_error(&bs), true );
test_assert( bs_eof(&bs), true );
test_assert(bs_pos( &bs ), 16);
return 0;
}
#undef bs_init
static size_t bs_skipeven_bytes_forward( bs_t *s, size_t i_count )
{
if( s->p_start == NULL || (s->p_start + 1) > s->p_end )
return 0;
if( s->p == NULL )
{
s->p = s->p_start;
return 1 + bs_skipeven_bytes_forward( s, i_count - 1 );
}
if( s->p_end - s->p > (ssize_t) i_count * 2 )
{
s->p += i_count * 2;
return i_count;
}
else
{
s->p = s->p_end;
return 0;
}
}
static size_t bs_skipeven_bytes_pos( const bs_t *s )
{
if( s->p )
return s->p + 1 < s->p_end ? (s->p - s->p_start) / 2 + 1 : (s->p - s->p_start) / 2;
else
return 0;
}
bs_byte_callbacks_t skipeven_cb = {
bs_skipeven_bytes_forward,
bs_skipeven_bytes_pos,
};
static int test_annexb( const char *psz_tag )
{
const uint8_t annexb[] = { 0xFF, 0x00, 0x00, 0x03, 0x01, 0xFF,
0x03, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x03 };
const uint8_t unesc[] = { 0xFF, 0x00, 0x00, 0x01, 0xFF,
0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03 };
bs_t bs;
struct hxxx_bsfw_ep3b_ctx_s bsctx;
hxxx_bsfw_ep3b_ctx_init( &bsctx );
bs_init_custom( &bs, &annexb, ARRAY_SIZE(annexb), &hxxx_bsfw_ep3b_callbacks, &bsctx );
for( size_t i=0; i<ARRAY_SIZE(unesc)*8; i++ )
{
test_assert(bs_aligned( &bs ), !!(i%8 == 0));
test_assert(bs_pos( &bs ), i);
bs_read( &bs, 1 );
}
test_assert(bs_eof( &bs ), 1);
hxxx_bsfw_ep3b_ctx_init( &bsctx );
bs_init_custom( &bs, &annexb, ARRAY_SIZE(annexb), &hxxx_bsfw_ep3b_callbacks, &bsctx );
for( size_t i=0; i<ARRAY_SIZE(unesc)*4; i++ )
{
test_assert(bs_pos( &bs ), i*2);
bs_read( &bs, 2 );
}
test_assert(bs_eof( &bs ), 1);
/* overflows */
bs_init( &bs, &annexb, ARRAY_SIZE(annexb) );
bs_skip( &bs, (ARRAY_SIZE(annexb) + 1) * 8 );
test_assert(bs_pos( &bs ), ARRAY_SIZE(annexb) * 8);
/* real world vps */
const uint8_t vpsnal[] = { 0x40, 0x01, 0x0C, 0x01, 0xFF, 0xFF, 0x01, 0x60,
0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x78, 0x10, 0x90, 0x24 };
const uint8_t vpsnalunesc[] = {
0x40, 0x01, 0x0C, 0x01, 0xFF, 0xFF, 0x01, 0x60,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x78, 0x10, 0x90, 0x24 };
hxxx_bsfw_ep3b_ctx_init( &bsctx );
bs_init_custom( &bs, vpsnal, ARRAY_SIZE(vpsnal), &hxxx_bsfw_ep3b_callbacks, &bsctx );
for(size_t i=0; i<ARRAY_SIZE(vpsnalunesc); i++)
test_assert(bs_read(&bs, 8), vpsnalunesc[i]);
/* real world sei, triggered double unescape bug */
const uint8_t seinal[] = { 0x4E, 0x01, 0x01, 0x09, 0xB0, 0x00, 0x25, 0x70,
0xE2, 0x00, 0x00, 0x03, 0x00, 0x03, 0x88, 0x06,
0x60, 0x40, 0xF1, 0x4C, 0xB8, 0x10, 0x05, 0x1C,
0xA8, 0x68, 0x7D, 0xD4, 0xD7, 0x59, 0x37, 0x58,
0xA5, 0xCE, 0xF0, 0x33, 0x8B, 0x65, 0x45, 0xF1,
0x1F, 0x00, 0x05, 0xFF, 0xA3, 0x55, 0xFF, 0x15,
0x6D, 0xFF, 0xB0, 0x9D, 0x05, 0x1C, 0xDA, 0x84,
0x22, 0x1F, 0x18, 0xEC, 0x53, 0x1A, 0x8E, 0x05,
0x2C, 0x6D, 0xD1, 0xBF, 0x54, 0x3A, 0x1F, 0x16,
0x06, 0xFF, 0x04, 0x5B, 0xFF, 0xB4, 0x79, 0xFF,
0xE8, 0x14, 0x80 };
const uint8_t seinalunesc[] =
{ 0x4E, 0x01, 0x01, 0x09, 0xB0, 0x00, 0x25, 0x70,
0xE2, 0x00, 0x00, 0x00, 0x03, 0x88, 0x06,
0x60, 0x40, 0xF1, 0x4C, 0xB8, 0x10, 0x05, 0x1C,
0xA8, 0x68, 0x7D, 0xD4, 0xD7, 0x59, 0x37, 0x58,
0xA5, 0xCE, 0xF0, 0x33, 0x8B, 0x65, 0x45, 0xF1,
0x1F, 0x00, 0x05, 0xFF, 0xA3, 0x55, 0xFF, 0x15,
0x6D, 0xFF, 0xB0, 0x9D, 0x05, 0x1C, 0xDA, 0x84,
0x22, 0x1F, 0x18, 0xEC, 0x53, 0x1A, 0x8E, 0x05,
0x2C, 0x6D, 0xD1, 0xBF, 0x54, 0x3A, 0x1F, 0x16,
0x06, 0xFF, 0x04, 0x5B, 0xFF, 0xB4, 0x79, 0xFF,
0xE8, 0x14, 0x80 };
hxxx_bsfw_ep3b_ctx_init( &bsctx );
bs_init_custom( &bs, seinal, ARRAY_SIZE(seinal), &hxxx_bsfw_ep3b_callbacks, &bsctx );
for(size_t i=0; i<ARRAY_SIZE(seinalunesc); i++)
test_assert(bs_read(&bs, 8), seinalunesc[i]);
return 0;
}
int main( void )
{
test_init();
struct testset testsets[TESTSET_COUNT] =
{
[TESTSET0] =
{ 1, { 0x00 } },
[TESTSET1] =
{ 2, { 0xAA, 0x55 } },
[TESTSET_EXPGOLOMB] =
{ 2, { 0x15, 0x23 } },
[TESTSET2] =
{ 6, { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF } },
};
if( run_tests( testsets, "normal", NULL, NULL, NULL ) )
return 1;
struct testset skipeventestsets[TESTSET_COUNT] =
{
[TESTSET0] =
{ 2, { 0x00, 0x00 } },
[TESTSET1] =
{ 4, { 0xAA, 0xFF, 0x55, 0xFF } },
[TESTSET_EXPGOLOMB] =
{ 4, { 0x15, 0xFF, 0x23, 0xFF } },
[TESTSET2] =
{ 12, { 0xAA, 0xFF, 0xBB, 0xFF, 0xCC, 0xFF, 0xDD, 0xFF, 0xEE, 0xFF, 0xFF, 0xFF } },
};
if( run_tests( skipeventestsets, "even byte skip", &skipeven_cb, NULL, NULL ) )
return 1;
if( test_annexb( "annexb ") )
return 1;
return 0;
}
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|