mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-05-21 18:20:41 +00:00
287 lines
8.1 KiB
C++
287 lines
8.1 KiB
C++
/*
|
|
* test_bs_copy_bits.cpp — Unit test for bs_copy_bits bulk bit copy
|
|
*
|
|
* Standalone test (no framework). Return 0=PASS, 1=FAIL.
|
|
*/
|
|
|
|
#include "mbs_mux_common.h"
|
|
#include "bs.h"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
|
|
using namespace subcodec::mux;
|
|
|
|
static int failures = 0;
|
|
|
|
#define CHECK(cond, msg) do { \
|
|
if (!(cond)) { \
|
|
printf(" FAIL: %s\n", msg); \
|
|
failures++; \
|
|
return; \
|
|
} \
|
|
} while(0)
|
|
|
|
/* Helper: count bits written to a bs_t */
|
|
static int bs_bits_written(bs_t* b) {
|
|
return (int)(b->p - b->start) * 8 + (8 - b->bits_left);
|
|
}
|
|
|
|
/* Helper: extract bit i from a byte array (MSB-first) */
|
|
static int get_bit(const uint8_t* data, int bit_idx) {
|
|
return (data[bit_idx / 8] >> (7 - (bit_idx % 8))) & 1;
|
|
}
|
|
|
|
/* ---- Test: aligned copy ---- */
|
|
static void test_aligned_copy() {
|
|
printf("test_aligned_copy: ");
|
|
|
|
uint8_t src[4] = {0xDE, 0xAD, 0xBE, 0xEF};
|
|
uint8_t dst_buf[8] = {};
|
|
bs_t dst;
|
|
bs_init(&dst, dst_buf, sizeof(dst_buf));
|
|
|
|
bs_copy_bits(&dst, src, 0, 32);
|
|
|
|
CHECK(bs_bits_written(&dst) == 32, "should write 32 bits");
|
|
CHECK(dst_buf[0] == 0xDE, "byte 0");
|
|
CHECK(dst_buf[1] == 0xAD, "byte 1");
|
|
CHECK(dst_buf[2] == 0xBE, "byte 2");
|
|
CHECK(dst_buf[3] == 0xEF, "byte 3");
|
|
|
|
printf("PASS\n");
|
|
}
|
|
|
|
/* ---- Test: offset copy ---- */
|
|
static void test_offset_copy() {
|
|
printf("test_offset_copy: ");
|
|
|
|
// src = 0xDE = 1101_1110, we skip 4 bits, read 12 bits
|
|
// bits at offset 4: 1110 + next 8 bits of 0xAD = 1010_1101
|
|
// so 12 bits = 1110_1010_1101
|
|
uint8_t src[4] = {0xDE, 0xAD, 0xBE, 0xEF};
|
|
uint8_t dst_buf[8] = {};
|
|
bs_t dst;
|
|
bs_init(&dst, dst_buf, sizeof(dst_buf));
|
|
|
|
bs_copy_bits(&dst, src, 4, 12);
|
|
|
|
CHECK(bs_bits_written(&dst) == 12, "should write 12 bits");
|
|
|
|
// Verify bit by bit
|
|
for (int i = 0; i < 12; i++) {
|
|
int expected = get_bit(src, 4 + i);
|
|
int actual = get_bit(dst_buf, i);
|
|
if (expected != actual) {
|
|
printf(" FAIL: bit %d expected %d got %d\n", i, expected, actual);
|
|
failures++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf("PASS\n");
|
|
}
|
|
|
|
/* ---- Test: unaligned dst ---- */
|
|
static void test_unaligned_dst() {
|
|
printf("test_unaligned_dst: ");
|
|
|
|
uint8_t src[4] = {0xDE, 0xAD, 0xBE, 0xEF};
|
|
uint8_t dst_buf[8] = {};
|
|
bs_t dst;
|
|
bs_init(&dst, dst_buf, sizeof(dst_buf));
|
|
|
|
// Write 3 bits first to misalign dst
|
|
bs_write_u(&dst, 3, 0x5); // 101
|
|
|
|
bs_copy_bits(&dst, src, 0, 16);
|
|
|
|
CHECK(bs_bits_written(&dst) == 19, "should write 3+16=19 bits");
|
|
|
|
// Verify: first 3 bits = 101, then 16 bits of 0xDEAD
|
|
// Bit-by-bit check
|
|
// dst_buf should start with: 101_11011_110_10101_101...
|
|
// = 1011_1011 1101_0101 101x_xxxx
|
|
// First verify the 16 copied bits match src
|
|
for (int i = 0; i < 16; i++) {
|
|
int expected = get_bit(src, i);
|
|
int actual = get_bit(dst_buf, 3 + i);
|
|
if (expected != actual) {
|
|
printf(" FAIL: bit %d expected %d got %d\n", i, expected, actual);
|
|
failures++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf("PASS\n");
|
|
}
|
|
|
|
/* ---- Test: zero bits ---- */
|
|
static void test_zero_bits() {
|
|
printf("test_zero_bits: ");
|
|
|
|
uint8_t src[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
|
uint8_t dst_buf[8] = {};
|
|
bs_t dst;
|
|
bs_init(&dst, dst_buf, sizeof(dst_buf));
|
|
|
|
bs_copy_bits(&dst, src, 0, 0);
|
|
|
|
CHECK(bs_bits_written(&dst) == 0, "should write 0 bits");
|
|
CHECK(dst_buf[0] == 0, "dst should be untouched");
|
|
|
|
printf("PASS\n");
|
|
}
|
|
|
|
/* ---- Test: round-trip vs bit-by-bit ---- */
|
|
static void test_round_trip() {
|
|
printf("test_round_trip (100 trials): ");
|
|
|
|
srand(42);
|
|
|
|
for (int trial = 0; trial < 100; trial++) {
|
|
int src_offset = rand() % 8;
|
|
int nbits = 1 + (rand() % 64);
|
|
|
|
// Need enough source bytes
|
|
int src_bytes = (src_offset + nbits + 7) / 8 + 1;
|
|
uint8_t src[16] = {};
|
|
for (int i = 0; i < src_bytes && i < 16; i++) {
|
|
src[i] = (uint8_t)(rand() & 0xFF);
|
|
}
|
|
|
|
// Reference: bit-by-bit copy
|
|
uint8_t ref_buf[16] = {};
|
|
bs_t ref;
|
|
bs_init(&ref, ref_buf, sizeof(ref_buf));
|
|
{
|
|
// Use a read bs to extract bits
|
|
bs_t rd;
|
|
bs_init(&rd, src, sizeof(src));
|
|
// Skip to src_offset
|
|
for (int i = 0; i < src_offset; i++) bs_read_u1(&rd);
|
|
for (int i = 0; i < nbits; i++) {
|
|
bs_write_u1(&ref, bs_read_u1(&rd));
|
|
}
|
|
}
|
|
int ref_bits = bs_bits_written(&ref);
|
|
|
|
// Test: bs_copy_bits
|
|
uint8_t test_buf[16] = {};
|
|
bs_t test;
|
|
bs_init(&test, test_buf, sizeof(test_buf));
|
|
bs_copy_bits(&test, src, src_offset, nbits);
|
|
int test_bits = bs_bits_written(&test);
|
|
|
|
if (ref_bits != test_bits) {
|
|
printf(" FAIL trial %d: ref wrote %d bits, test wrote %d\n",
|
|
trial, ref_bits, test_bits);
|
|
failures++;
|
|
return;
|
|
}
|
|
|
|
int ref_bytes = (ref_bits + 7) / 8;
|
|
// Compare only the bits that were written (mask trailing bits in last byte)
|
|
for (int i = 0; i < ref_bytes; i++) {
|
|
uint8_t mask = 0xFF;
|
|
if (i == ref_bytes - 1) {
|
|
int tail = ref_bits % 8;
|
|
if (tail != 0) mask = (uint8_t)(0xFF << (8 - tail));
|
|
}
|
|
if ((ref_buf[i] & mask) != (test_buf[i] & mask)) {
|
|
printf(" FAIL trial %d: byte %d ref=0x%02X test=0x%02X (mask=0x%02X)\n",
|
|
trial, i, ref_buf[i] & mask, test_buf[i] & mask, mask);
|
|
printf(" src_offset=%d nbits=%d\n", src_offset, nbits);
|
|
failures++;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("PASS\n");
|
|
}
|
|
|
|
/* ---- Test: unaligned dst + offset src ---- */
|
|
static void test_both_unaligned() {
|
|
printf("test_both_unaligned: ");
|
|
|
|
srand(123);
|
|
|
|
for (int trial = 0; trial < 100; trial++) {
|
|
int dst_pre = 1 + (rand() % 7); // 1-7 bits pre-written to dst
|
|
int src_offset = rand() % 8;
|
|
int nbits = 1 + (rand() % 48);
|
|
|
|
int src_bytes = (src_offset + nbits + 7) / 8 + 1;
|
|
uint8_t src[16] = {};
|
|
for (int i = 0; i < src_bytes && i < 16; i++) {
|
|
src[i] = (uint8_t)(rand() & 0xFF);
|
|
}
|
|
|
|
uint8_t pre_val = (uint8_t)(rand() & ((1 << dst_pre) - 1));
|
|
|
|
// Reference: bit-by-bit
|
|
uint8_t ref_buf[16] = {};
|
|
bs_t ref;
|
|
bs_init(&ref, ref_buf, sizeof(ref_buf));
|
|
bs_write_u(&ref, dst_pre, pre_val);
|
|
{
|
|
bs_t rd;
|
|
bs_init(&rd, src, sizeof(src));
|
|
for (int i = 0; i < src_offset; i++) bs_read_u1(&rd);
|
|
for (int i = 0; i < nbits; i++) {
|
|
bs_write_u1(&ref, bs_read_u1(&rd));
|
|
}
|
|
}
|
|
int ref_bits = bs_bits_written(&ref);
|
|
|
|
// Test
|
|
uint8_t test_buf[16] = {};
|
|
bs_t test;
|
|
bs_init(&test, test_buf, sizeof(test_buf));
|
|
bs_write_u(&test, dst_pre, pre_val);
|
|
bs_copy_bits(&test, src, src_offset, nbits);
|
|
int test_bits = bs_bits_written(&test);
|
|
|
|
if (ref_bits != test_bits) {
|
|
printf(" FAIL trial %d: ref %d bits, test %d bits\n",
|
|
trial, ref_bits, test_bits);
|
|
failures++;
|
|
return;
|
|
}
|
|
|
|
int bytes = (ref_bits + 7) / 8;
|
|
for (int i = 0; i < bytes; i++) {
|
|
uint8_t mask = 0xFF;
|
|
if (i == bytes - 1) {
|
|
int tail = ref_bits % 8;
|
|
if (tail != 0) mask = (uint8_t)(0xFF << (8 - tail));
|
|
}
|
|
if ((ref_buf[i] & mask) != (test_buf[i] & mask)) {
|
|
printf(" FAIL trial %d: byte %d ref=0x%02X test=0x%02X\n",
|
|
trial, i, ref_buf[i] & mask, test_buf[i] & mask);
|
|
failures++;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("PASS\n");
|
|
}
|
|
|
|
int main() {
|
|
printf("=== test_bs_copy_bits ===\n");
|
|
|
|
test_aligned_copy();
|
|
test_offset_copy();
|
|
test_unaligned_dst();
|
|
test_zero_bits();
|
|
test_round_trip();
|
|
test_both_unaligned();
|
|
|
|
printf("\n%s (%d failure%s)\n",
|
|
failures ? "FAILED" : "ALL PASSED",
|
|
failures, failures == 1 ? "" : "s");
|
|
return failures ? 1 : 0;
|
|
}
|