LCOV - code coverage report
Current view: top level - tests/unit - test_tl_serial.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 612 612
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 59 59

            Line data    Source code
       1              : /**
       2              :  * @file test_tl_serial.c
       3              :  * @brief Unit tests for TL binary serialization.
       4              :  */
       5              : 
       6              : #include "test_helpers.h"
       7              : #include "tl_serial.h"
       8              : 
       9              : #include <stdlib.h>
      10              : #include <string.h>
      11              : #include <math.h>
      12              : 
      13              : /* Forward declarations of individual test functions */
      14              : void test_tl_writer_init_free(void);
      15              : void test_tl_write_int32(void);
      16              : void test_tl_write_int32_negative(void);
      17              : void test_tl_write_uint32(void);
      18              : void test_tl_write_int64(void);
      19              : void test_tl_write_uint64(void);
      20              : void test_tl_write_int128(void);
      21              : void test_tl_write_int256(void);
      22              : void test_tl_write_double(void);
      23              : void test_tl_write_bool_true(void);
      24              : void test_tl_write_bool_false(void);
      25              : void test_tl_write_string_short(void);
      26              : void test_tl_write_string_empty(void);
      27              : void test_tl_write_string_needs_padding(void);
      28              : void test_tl_write_string_aligned(void);
      29              : void test_tl_write_bytes_long(void);
      30              : void test_tl_write_vector_begin(void);
      31              : void test_tl_write_multiple(void);
      32              : void test_tl_reader_init(void);
      33              : void test_tl_reader_ok(void);
      34              : void test_tl_read_int32(void);
      35              : void test_tl_read_uint32(void);
      36              : void test_tl_read_int64(void);
      37              : void test_tl_read_int128(void);
      38              : void test_tl_read_int256(void);
      39              : void test_tl_read_bool_true(void);
      40              : void test_tl_read_bool_false(void);
      41              : void test_tl_read_bool_invalid(void);
      42              : void test_tl_read_string_short(void);
      43              : void test_tl_read_string_empty(void);
      44              : void test_tl_read_bytes_with_padding(void);
      45              : void test_tl_read_bytes_empty(void);
      46              : void test_tl_read_bytes_long_prefix(void);
      47              : void test_tl_read_past_end(void);
      48              : void test_tl_read_skip(void);
      49              : void test_tl_read_raw(void);
      50              : void test_tl_roundtrip_int32(void);
      51              : void test_tl_roundtrip_uint64(void);
      52              : void test_tl_roundtrip_string(void);
      53              : void test_tl_roundtrip_int128_int256(void);
      54              : void test_tl_roundtrip_double(void);
      55              : void test_tl_roundtrip_bool(void);
      56              : void test_tl_roundtrip_mixed(void);
      57              : void test_tl_string_null(void);
      58              : void test_tl_writer_grow(void);
      59              : void test_tl_vector_overflow_uint32(void);
      60              : void test_tl_vector_overflow_int32(void);
      61              : void test_tl_vector_overflow_uint64(void);
      62              : void test_tl_vector_overflow_string(void);
      63              : void test_tl_vector_overflow_reader_saturates(void);
      64              : void test_tl_read_bytes_giant_length_claim(void);
      65              : void test_tl_read_bytes_long_prefix_exact(void);
      66              : void test_tl_read_bytes_invalid_ff_prefix(void);
      67              : void test_tl_string_roundtrip_lengths(void);
      68              : void test_tl_int32_roundtrip_boundaries(void);
      69              : void test_tl_int64_roundtrip_boundaries(void);
      70              : void test_tl_vector_roundtrip_empty_single_many(void);
      71              : 
      72              : /* ================================================================
      73              :  * Writer tests
      74              :  * ================================================================ */
      75              : 
      76            1 : void test_tl_writer_init_free(void) {
      77              :     TlWriter w;
      78            1 :     tl_writer_init(&w);
      79            1 :     ASSERT(w.data == NULL, "data should be NULL after init");
      80            1 :     ASSERT(w.len == 0, "len should be 0 after init");
      81            1 :     ASSERT(w.cap == 0, "cap should be 0 after init");
      82            1 :     tl_writer_free(&w);
      83              : }
      84              : 
      85            1 : void test_tl_write_int32(void) {
      86              :     TlWriter w;
      87            1 :     tl_writer_init(&w);
      88            1 :     tl_write_int32(&w, 0x01020304);
      89            1 :     ASSERT(w.len == 4, "should have 4 bytes");
      90              :     /* Little-endian: 04 03 02 01 */
      91            1 :     ASSERT(w.data[0] == 0x04, "byte 0 should be 0x04");
      92            1 :     ASSERT(w.data[1] == 0x03, "byte 1 should be 0x03");
      93            1 :     ASSERT(w.data[2] == 0x02, "byte 2 should be 0x02");
      94            1 :     ASSERT(w.data[3] == 0x01, "byte 3 should be 0x01");
      95            1 :     tl_writer_free(&w);
      96              : }
      97              : 
      98            1 : void test_tl_write_int32_negative(void) {
      99              :     TlWriter w;
     100            1 :     tl_writer_init(&w);
     101            1 :     tl_write_int32(&w, -1);
     102            1 :     ASSERT(w.len == 4, "should have 4 bytes");
     103            1 :     ASSERT(w.data[0] == 0xFF && w.data[1] == 0xFF
     104              :         && w.data[2] == 0xFF && w.data[3] == 0xFF,
     105              :            "-1 should be 0xFFFFFFFF in LE");
     106            1 :     tl_writer_free(&w);
     107              : }
     108              : 
     109            1 : void test_tl_write_uint32(void) {
     110              :     TlWriter w;
     111            1 :     tl_writer_init(&w);
     112            1 :     tl_write_uint32(&w, 0xABCDEF01u);
     113            1 :     ASSERT(w.len == 4, "should have 4 bytes");
     114            1 :     ASSERT(w.data[0] == 0x01, "byte 0 LE");
     115            1 :     ASSERT(w.data[3] == 0xAB, "byte 3 LE");
     116            1 :     tl_writer_free(&w);
     117              : }
     118              : 
     119            1 : void test_tl_write_int64(void) {
     120              :     TlWriter w;
     121            1 :     tl_writer_init(&w);
     122            1 :     tl_write_int64(&w, 0x0102030405060708LL);
     123            1 :     ASSERT(w.len == 8, "should have 8 bytes");
     124            1 :     ASSERT(w.data[0] == 0x08, "byte 0 LE");
     125            1 :     ASSERT(w.data[7] == 0x01, "byte 7 LE");
     126            1 :     tl_writer_free(&w);
     127              : }
     128              : 
     129            1 : void test_tl_write_uint64(void) {
     130              :     TlWriter w;
     131            1 :     tl_writer_init(&w);
     132            1 :     tl_write_uint64(&w, 0xFEDCBA9876543210ULL);
     133            1 :     ASSERT(w.len == 8, "should have 8 bytes");
     134            1 :     ASSERT(w.data[0] == 0x10, "byte 0 LE");
     135            1 :     ASSERT(w.data[7] == 0xFE, "byte 7 LE");
     136            1 :     tl_writer_free(&w);
     137              : }
     138              : 
     139            1 : void test_tl_write_int128(void) {
     140              :     TlWriter w;
     141            1 :     tl_writer_init(&w);
     142              :     unsigned char val[16];
     143           17 :     for (int i = 0; i < 16; i++) val[i] = (unsigned char)(i + 1);
     144            1 :     tl_write_int128(&w, val);
     145            1 :     ASSERT(w.len == 16, "should have 16 bytes");
     146            1 :     ASSERT(w.data[0] == 1, "first byte should be 1");
     147            1 :     ASSERT(w.data[15] == 16, "last byte should be 16");
     148            1 :     tl_writer_free(&w);
     149              : }
     150              : 
     151            1 : void test_tl_write_int256(void) {
     152              :     TlWriter w;
     153            1 :     tl_writer_init(&w);
     154              :     unsigned char val[32];
     155           33 :     for (int i = 0; i < 32; i++) val[i] = (unsigned char)i;
     156            1 :     tl_write_int256(&w, val);
     157            1 :     ASSERT(w.len == 32, "should have 32 bytes");
     158            1 :     ASSERT(w.data[0] == 0, "first byte should be 0");
     159            1 :     ASSERT(w.data[31] == 31, "last byte should be 31");
     160            1 :     tl_writer_free(&w);
     161              : }
     162              : 
     163            1 : void test_tl_write_double(void) {
     164              :     TlWriter w;
     165            1 :     tl_writer_init(&w);
     166            1 :     tl_write_double(&w, 3.14);
     167            1 :     ASSERT(w.len == 8, "should have 8 bytes");
     168              :     /* Verify by reading back */
     169            1 :     TlReader r = tl_reader_init(w.data, w.len);
     170            1 :     double val = tl_read_double(&r);
     171            1 :     ASSERT(fabs(val - 3.14) < 1e-10, "round-trip should preserve value");
     172            1 :     tl_writer_free(&w);
     173              : }
     174              : 
     175            1 : void test_tl_write_bool_true(void) {
     176              :     TlWriter w;
     177            1 :     tl_writer_init(&w);
     178            1 :     tl_write_bool(&w, 1);
     179            1 :     ASSERT(w.len == 4, "should have 4 bytes");
     180              :     /* 0x997275b5 in LE */
     181            1 :     ASSERT(w.data[0] == 0xB5, "bool true magic byte 0");
     182            1 :     ASSERT(w.data[1] == 0x75, "bool true magic byte 1");
     183            1 :     ASSERT(w.data[2] == 0x72, "bool true magic byte 2");
     184            1 :     ASSERT(w.data[3] == 0x99, "bool true magic byte 3");
     185            1 :     tl_writer_free(&w);
     186              : }
     187              : 
     188            1 : void test_tl_write_bool_false(void) {
     189              :     TlWriter w;
     190            1 :     tl_writer_init(&w);
     191            1 :     tl_write_bool(&w, 0);
     192            1 :     ASSERT(w.len == 4, "should have 4 bytes");
     193              :     /* 0xbc799737 in LE */
     194            1 :     ASSERT(w.data[0] == 0x37, "bool false magic byte 0");
     195            1 :     ASSERT(w.data[1] == 0x97, "bool false magic byte 1");
     196            1 :     ASSERT(w.data[2] == 0x79, "bool false magic byte 2");
     197            1 :     ASSERT(w.data[3] == 0xBC, "bool false magic byte 3");
     198            1 :     tl_writer_free(&w);
     199              : }
     200              : 
     201            1 : void test_tl_write_string_short(void) {
     202              :     TlWriter w;
     203            1 :     tl_writer_init(&w);
     204            1 :     tl_write_string(&w, "abc");
     205              :     /* 1-byte prefix (3) + 3 bytes data + 0 padding (total 4, aligned) = 4 bytes */
     206            1 :     ASSERT(w.len == 4, "short string should be 4 bytes (1 prefix + 3 data)");
     207            1 :     ASSERT(w.data[0] == 3, "length prefix should be 3");
     208            1 :     ASSERT(w.data[1] == 'a', "first char");
     209            1 :     ASSERT(w.data[2] == 'b', "second char");
     210            1 :     ASSERT(w.data[3] == 'c', "third char");
     211            1 :     tl_writer_free(&w);
     212              : }
     213              : 
     214            1 : void test_tl_write_string_empty(void) {
     215              :     TlWriter w;
     216            1 :     tl_writer_init(&w);
     217            1 :     tl_write_string(&w, "");
     218              :     /* 1-byte prefix (0) + 0 data + 3 padding = 4 bytes */
     219            1 :     ASSERT(w.len == 4, "empty string should be 4 bytes (1 prefix + 3 pad)");
     220            1 :     ASSERT(w.data[0] == 0, "length prefix should be 0");
     221            1 :     tl_writer_free(&w);
     222              : }
     223              : 
     224            1 : void test_tl_write_string_needs_padding(void) {
     225              :     TlWriter w;
     226            1 :     tl_writer_init(&w);
     227            1 :     tl_write_string(&w, "a");
     228              :     /* 1-byte prefix (1) + 1 byte data + 2 padding = 4 bytes */
     229            1 :     ASSERT(w.len == 4, "1-char string should be 4 bytes (1 prefix + 1 data + 2 pad)");
     230            1 :     ASSERT(w.data[0] == 1, "length prefix should be 1");
     231            1 :     ASSERT(w.data[1] == 'a', "char data");
     232            1 :     ASSERT(w.data[2] == 0, "pad byte 1");
     233            1 :     ASSERT(w.data[3] == 0, "pad byte 2");
     234            1 :     tl_writer_free(&w);
     235              : }
     236              : 
     237            1 : void test_tl_write_string_aligned(void) {
     238              :     TlWriter w;
     239            1 :     tl_writer_init(&w);
     240            1 :     tl_write_string(&w, "abcde");
     241              :     /* 1-byte prefix (5) + 5 bytes data + 2 padding = 8 bytes */
     242            1 :     ASSERT(w.len == 8, "5-char string should be 8 bytes");
     243            1 :     ASSERT(w.data[0] == 5, "length prefix should be 5");
     244            1 :     tl_writer_free(&w);
     245              : }
     246              : 
     247            1 : void test_tl_write_bytes_long(void) {
     248              :     TlWriter w;
     249            1 :     tl_writer_init(&w);
     250              :     /* 256 bytes — needs 4-byte length prefix (>= 254) */
     251              :     unsigned char data[256];
     252            1 :     memset(data, 0xAB, sizeof(data));
     253            1 :     tl_write_bytes(&w, data, 256);
     254              :     /* 4-byte prefix + 256 data + 0 padding (260 is 4-aligned) = 260 */
     255            1 :     ASSERT(w.len == 260, "256-byte data should be 260 bytes");
     256            1 :     ASSERT(w.data[0] == 0xFE, "first byte signals long prefix");
     257            1 :     ASSERT(w.data[1] == 0x00 && w.data[2] == 0x01 && w.data[3] == 0x00,
     258              :            "3-byte length should be 256 (LE: 00 01 00)");
     259            1 :     ASSERT(w.data[4] == 0xAB, "data starts at offset 4");
     260            1 :     tl_writer_free(&w);
     261              : }
     262              : 
     263            1 : void test_tl_write_vector_begin(void) {
     264              :     TlWriter w;
     265            1 :     tl_writer_init(&w);
     266            1 :     tl_write_vector_begin(&w, 3);
     267            1 :     ASSERT(w.len == 8, "vector header should be 8 bytes (ctor + count)");
     268              :     /* Constructor 0x1cb5c415 in LE */
     269            1 :     ASSERT(w.data[0] == 0x15, "vector ctor byte 0");
     270            1 :     ASSERT(w.data[4] == 3, "count should be 3");
     271            1 :     tl_writer_free(&w);
     272              : }
     273              : 
     274            1 : void test_tl_write_multiple(void) {
     275              :     TlWriter w;
     276            1 :     tl_writer_init(&w);
     277            1 :     tl_write_int32(&w, 1);
     278            1 :     tl_write_int32(&w, 2);
     279            1 :     tl_write_int32(&w, 3);
     280            1 :     ASSERT(w.len == 12, "three int32s should be 12 bytes");
     281            1 :     tl_writer_free(&w);
     282              : }
     283              : 
     284              : /* ================================================================
     285              :  * Reader tests
     286              :  * ================================================================ */
     287              : 
     288            1 : void test_tl_reader_init(void) {
     289            1 :     unsigned char buf[4] = {0};
     290            1 :     TlReader r = tl_reader_init(buf, 4);
     291            1 :     ASSERT(r.data == buf, "data should point to buffer");
     292            1 :     ASSERT(r.len == 4, "len should be 4");
     293            1 :     ASSERT(r.pos == 0, "pos should be 0");
     294              : }
     295              : 
     296            1 : void test_tl_reader_ok(void) {
     297            1 :     unsigned char buf[4] = {1, 2, 3, 4};
     298            1 :     TlReader r = tl_reader_init(buf, 4);
     299            1 :     ASSERT(tl_reader_ok(&r), "should be ok at start");
     300            1 :     r.pos = 4;
     301            1 :     ASSERT(!tl_reader_ok(&r), "should not be ok at end");
     302              : }
     303              : 
     304            1 : void test_tl_read_int32(void) {
     305            1 :     unsigned char buf[4] = {0x04, 0x03, 0x02, 0x01};
     306            1 :     TlReader r = tl_reader_init(buf, 4);
     307            1 :     int32_t val = tl_read_int32(&r);
     308            1 :     ASSERT(val == 0x01020304, "should read LE int32");
     309            1 :     ASSERT(r.pos == 4, "pos should advance by 4");
     310              : }
     311              : 
     312            1 : void test_tl_read_uint32(void) {
     313            1 :     unsigned char buf[4] = {0x01, 0xEF, 0xCD, 0xAB};
     314            1 :     TlReader r = tl_reader_init(buf, 4);
     315            1 :     uint32_t val = tl_read_uint32(&r);
     316            1 :     ASSERT(val == 0xABCDEF01u, "should read LE uint32");
     317              : }
     318              : 
     319            1 : void test_tl_read_int64(void) {
     320            1 :     unsigned char buf[8] = {0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
     321            1 :     TlReader r = tl_reader_init(buf, 8);
     322            1 :     int64_t val = tl_read_int64(&r);
     323            1 :     ASSERT(val == 0x0102030405060708LL, "should read LE int64");
     324              : }
     325              : 
     326            1 : void test_tl_read_int128(void) {
     327              :     unsigned char buf[16];
     328           17 :     for (int i = 0; i < 16; i++) buf[i] = (unsigned char)(i + 10);
     329            1 :     TlReader r = tl_reader_init(buf, 16);
     330            1 :     unsigned char out[16] = {0};
     331            1 :     tl_read_int128(&r, out);
     332            1 :     ASSERT(memcmp(out, buf, 16) == 0, "should read 16 raw bytes");
     333            1 :     ASSERT(r.pos == 16, "pos should advance by 16");
     334              : }
     335              : 
     336            1 : void test_tl_read_int256(void) {
     337              :     unsigned char buf[32];
     338           33 :     for (int i = 0; i < 32; i++) buf[i] = (unsigned char)(i + 5);
     339            1 :     TlReader r = tl_reader_init(buf, 32);
     340            1 :     unsigned char out[32] = {0};
     341            1 :     tl_read_int256(&r, out);
     342            1 :     ASSERT(memcmp(out, buf, 32) == 0, "should read 32 raw bytes");
     343              : }
     344              : 
     345            1 : void test_tl_read_bool_true(void) {
     346            1 :     unsigned char buf[4] = {0xB5, 0x75, 0x72, 0x99};
     347            1 :     TlReader r = tl_reader_init(buf, 4);
     348            1 :     ASSERT(tl_read_bool(&r) == 1, "should read bool true");
     349              : }
     350              : 
     351            1 : void test_tl_read_bool_false(void) {
     352            1 :     unsigned char buf[4] = {0x37, 0x97, 0x79, 0xBC};
     353            1 :     TlReader r = tl_reader_init(buf, 4);
     354            1 :     ASSERT(tl_read_bool(&r) == 0, "should read bool false");
     355              : }
     356              : 
     357            1 : void test_tl_read_bool_invalid(void) {
     358            1 :     unsigned char buf[4] = {0x00, 0x00, 0x00, 0x00};
     359            1 :     TlReader r = tl_reader_init(buf, 4);
     360            1 :     ASSERT(tl_read_bool(&r) == -1, "should return -1 for invalid bool magic");
     361              : }
     362              : 
     363            1 : void test_tl_read_string_short(void) {
     364              :     /* 1-byte prefix (3) + "abc" */
     365            1 :     unsigned char buf[] = {3, 'a', 'b', 'c'};
     366            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     367            1 :     char *s = tl_read_string(&r);
     368            1 :     ASSERT(s != NULL, "string should not be NULL");
     369            1 :     ASSERT(strcmp(s, "abc") == 0, "should read 'abc'");
     370            1 :     ASSERT(r.pos == 4, "pos should advance past padded area");
     371            1 :     free(s);
     372              : }
     373              : 
     374            1 : void test_tl_read_string_empty(void) {
     375              :     /* 1-byte prefix (0) + 3 padding */
     376            1 :     unsigned char buf[] = {0, 0, 0, 0};
     377            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     378            1 :     char *s = tl_read_string(&r);
     379            1 :     ASSERT(s != NULL, "empty string should not be NULL");
     380            1 :     ASSERT(s[0] == '\0', "empty string should be empty");
     381            1 :     ASSERT(r.pos == 4, "pos should advance past padding");
     382            1 :     free(s);
     383              : }
     384              : 
     385            1 : void test_tl_read_bytes_with_padding(void) {
     386              :     /* 1-byte prefix (5) + "abcde" + 2 pad bytes */
     387            1 :     unsigned char buf[] = {5, 'a', 'b', 'c', 'd', 'e', 0, 0};
     388            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     389            1 :     size_t len = 0;
     390            1 :     unsigned char *b = tl_read_bytes(&r, &len);
     391            1 :     ASSERT(b != NULL, "bytes should not be NULL");
     392            1 :     ASSERT(len == 5, "length should be 5");
     393            1 :     ASSERT(memcmp(b, "abcde", 5) == 0, "content should be 'abcde'");
     394            1 :     ASSERT(r.pos == 8, "pos should advance past padding");
     395            1 :     free(b);
     396              : }
     397              : 
     398            1 : void test_tl_read_bytes_empty(void) {
     399              :     /* Zero-length bytes: 1-byte prefix (0) + 3 pad bytes.
     400              :      * Regression: QA-21 — tl_read_bytes must return a non-NULL pointer for
     401              :      * empty payloads even on platforms where malloc(0) returns NULL. */
     402              :     TlWriter w;
     403            1 :     tl_writer_init(&w);
     404            1 :     tl_write_bytes(&w, NULL, 0);
     405            1 :     ASSERT(w.len == 4, "empty bytes should serialize to 4 bytes");
     406              : 
     407            1 :     TlReader r = tl_reader_init(w.data, w.len);
     408            1 :     size_t len = 42;
     409            1 :     unsigned char *b = tl_read_bytes(&r, &len);
     410            1 :     ASSERT(b != NULL, "empty bytes should return non-NULL pointer");
     411            1 :     ASSERT(len == 0, "length should be 0");
     412            1 :     ASSERT(r.pos == 4, "pos should advance past padding");
     413            1 :     free(b);
     414            1 :     tl_writer_free(&w);
     415              : }
     416              : 
     417            1 : void test_tl_read_bytes_long_prefix(void) {
     418              :     /* 4-byte prefix: 0xFE, 0x00, 0x01, 0x00 = 256 bytes */
     419            1 :     size_t total = 4 + 256;
     420            1 :     unsigned char *buf = (unsigned char *)calloc(1, total);
     421            1 :     buf[0] = 0xFE;
     422            1 :     buf[1] = 0x00;
     423            1 :     buf[2] = 0x01;
     424            1 :     buf[3] = 0x00;
     425            1 :     memset(buf + 4, 'X', 256);
     426              : 
     427            1 :     TlReader r = tl_reader_init(buf, total);
     428            1 :     size_t len = 0;
     429            1 :     unsigned char *b = tl_read_bytes(&r, &len);
     430            1 :     ASSERT(b != NULL, "long bytes should not be NULL");
     431            1 :     ASSERT(len == 256, "length should be 256");
     432            1 :     ASSERT(b[0] == 'X' && b[255] == 'X', "content should be all X");
     433            1 :     ASSERT(r.pos == total, "pos should advance to end");
     434            1 :     free(b);
     435            1 :     free(buf);
     436              : }
     437              : 
     438            1 : void test_tl_read_past_end(void) {
     439            1 :     unsigned char buf[2] = {1, 2};
     440            1 :     TlReader r = tl_reader_init(buf, 2);
     441            1 :     int32_t val = tl_read_int32(&r);
     442            1 :     ASSERT(val == 0, "reading past end should return 0");
     443            1 :     ASSERT(r.pos == r.len, "pos should be at end");
     444            1 :     ASSERT(!tl_reader_ok(&r), "reader should not be ok");
     445              : }
     446              : 
     447            1 : void test_tl_read_skip(void) {
     448            1 :     unsigned char buf[8] = {0};
     449            1 :     TlReader r = tl_reader_init(buf, 8);
     450            1 :     tl_read_skip(&r, 5);
     451            1 :     ASSERT(r.pos == 5, "pos should advance by 5");
     452            1 :     tl_read_skip(&r, 10);
     453            1 :     ASSERT(r.pos == 8, "pos should clamp to len");
     454              : }
     455              : 
     456            1 : void test_tl_read_raw(void) {
     457            1 :     unsigned char buf[4] = {0xAA, 0xBB, 0xCC, 0xDD};
     458            1 :     TlReader r = tl_reader_init(buf, 4);
     459            1 :     unsigned char out[4] = {0};
     460            1 :     tl_read_raw(&r, out, 4);
     461            1 :     ASSERT(memcmp(out, buf, 4) == 0, "raw read should copy bytes exactly");
     462              : }
     463              : 
     464              : /* ================================================================
     465              :  * Round-trip tests
     466              :  * ================================================================ */
     467              : 
     468            1 : void test_tl_roundtrip_int32(void) {
     469              :     TlWriter w;
     470            1 :     tl_writer_init(&w);
     471            1 :     int32_t values[] = {0, 1, -1, 0x7FFFFFFF, (int32_t)0x80000000};
     472            6 :     for (int i = 0; i < 5; i++) tl_write_int32(&w, values[i]);
     473              : 
     474            1 :     TlReader r = tl_reader_init(w.data, w.len);
     475            6 :     for (int i = 0; i < 5; i++) {
     476            5 :         ASSERT(tl_read_int32(&r) == values[i], "round-trip int32 mismatch");
     477              :     }
     478            1 :     ASSERT(!tl_reader_ok(&r), "reader should be at end");
     479            1 :     tl_writer_free(&w);
     480              : }
     481              : 
     482            1 : void test_tl_roundtrip_uint64(void) {
     483              :     TlWriter w;
     484            1 :     tl_writer_init(&w);
     485            1 :     tl_write_uint64(&w, 0);
     486            1 :     tl_write_uint64(&w, 0xFFFFFFFFFFFFFFFFULL);
     487              : 
     488            1 :     TlReader r = tl_reader_init(w.data, w.len);
     489            1 :     ASSERT(tl_read_uint64(&r) == 0, "round-trip uint64 zero");
     490            1 :     ASSERT(tl_read_uint64(&r) == 0xFFFFFFFFFFFFFFFFULL, "round-trip uint64 max");
     491            1 :     tl_writer_free(&w);
     492              : }
     493              : 
     494            1 : void test_tl_roundtrip_string(void) {
     495              :     TlWriter w;
     496            1 :     tl_writer_init(&w);
     497            1 :     tl_write_string(&w, "");
     498            1 :     tl_write_string(&w, "hello");
     499            1 :     tl_write_string(&w, "a longer string for testing purposes");
     500              : 
     501            1 :     TlReader r = tl_reader_init(w.data, w.len);
     502              :     char *s;
     503              : 
     504            1 :     s = tl_read_string(&r);
     505            1 :     ASSERT(s != NULL && strcmp(s, "") == 0, "round-trip empty string");
     506            1 :     free(s);
     507              : 
     508            1 :     s = tl_read_string(&r);
     509            1 :     ASSERT(s != NULL && strcmp(s, "hello") == 0, "round-trip 'hello'");
     510            1 :     free(s);
     511              : 
     512            1 :     s = tl_read_string(&r);
     513            1 :     ASSERT(s != NULL && strcmp(s, "a longer string for testing purposes") == 0,
     514              :            "round-trip longer string");
     515            1 :     free(s);
     516              : 
     517            1 :     ASSERT(!tl_reader_ok(&r), "reader should be at end");
     518            1 :     tl_writer_free(&w);
     519              : }
     520              : 
     521            1 : void test_tl_roundtrip_int128_int256(void) {
     522              :     TlWriter w;
     523            1 :     tl_writer_init(&w);
     524              :     unsigned char v128[16], v256[32];
     525           17 :     for (int i = 0; i < 16; i++) v128[i] = (unsigned char)(i * 17);
     526           33 :     for (int i = 0; i < 32; i++) v256[i] = (unsigned char)(i * 13);
     527              : 
     528            1 :     tl_write_int128(&w, v128);
     529            1 :     tl_write_int256(&w, v256);
     530              : 
     531            1 :     TlReader r = tl_reader_init(w.data, w.len);
     532              :     unsigned char o128[16], o256[32];
     533            1 :     tl_read_int128(&r, o128);
     534            1 :     tl_read_int256(&r, o256);
     535            1 :     ASSERT(memcmp(o128, v128, 16) == 0, "round-trip int128");
     536            1 :     ASSERT(memcmp(o256, v256, 32) == 0, "round-trip int256");
     537            1 :     tl_writer_free(&w);
     538              : }
     539              : 
     540            1 : void test_tl_roundtrip_double(void) {
     541              :     TlWriter w;
     542            1 :     tl_writer_init(&w);
     543            1 :     tl_write_double(&w, 0.0);
     544            1 :     tl_write_double(&w, -1.5);
     545            1 :     tl_write_double(&w, 1e20);
     546              : 
     547            1 :     TlReader r = tl_reader_init(w.data, w.len);
     548            1 :     ASSERT(tl_read_double(&r) == 0.0, "round-trip double 0.0");
     549            1 :     ASSERT(tl_read_double(&r) == -1.5, "round-trip double -1.5");
     550            1 :     ASSERT(fabs(tl_read_double(&r) - 1e20) < 1e10, "round-trip double 1e20");
     551            1 :     tl_writer_free(&w);
     552              : }
     553              : 
     554            1 : void test_tl_roundtrip_bool(void) {
     555              :     TlWriter w;
     556            1 :     tl_writer_init(&w);
     557            1 :     tl_write_bool(&w, 0);
     558            1 :     tl_write_bool(&w, 1);
     559            1 :     tl_write_bool(&w, 42); /* non-zero = true */
     560              : 
     561            1 :     TlReader r = tl_reader_init(w.data, w.len);
     562            1 :     ASSERT(tl_read_bool(&r) == 0, "round-trip bool false");
     563            1 :     ASSERT(tl_read_bool(&r) == 1, "round-trip bool true");
     564            1 :     ASSERT(tl_read_bool(&r) == 1, "round-trip bool 42→true");
     565            1 :     tl_writer_free(&w);
     566              : }
     567              : 
     568            1 : void test_tl_roundtrip_mixed(void) {
     569              :     TlWriter w;
     570            1 :     tl_writer_init(&w);
     571            1 :     tl_write_int32(&w, 0x12345678);
     572            1 :     tl_write_string(&w, "test");
     573            1 :     tl_write_bool(&w, 1);
     574            1 :     tl_write_uint64(&w, 999);
     575              : 
     576            1 :     TlReader r = tl_reader_init(w.data, w.len);
     577            1 :     ASSERT(tl_read_int32(&r) == 0x12345678, "mixed: int32");
     578            1 :     char *s = tl_read_string(&r);
     579            1 :     ASSERT(s != NULL && strcmp(s, "test") == 0, "mixed: string");
     580            1 :     free(s);
     581            1 :     ASSERT(tl_read_bool(&r) == 1, "mixed: bool");
     582            1 :     ASSERT(tl_read_uint64(&r) == 999, "mixed: uint64");
     583            1 :     ASSERT(!tl_reader_ok(&r), "mixed: reader at end");
     584            1 :     tl_writer_free(&w);
     585              : }
     586              : 
     587            1 : void test_tl_string_null(void) {
     588              :     TlWriter w;
     589            1 :     tl_writer_init(&w);
     590            1 :     tl_write_string(&w, NULL);
     591              :     /* Should behave like empty string */
     592            1 :     ASSERT(w.len == 4, "NULL string should be treated as empty (4 bytes)");
     593            1 :     ASSERT(w.data[0] == 0, "length prefix should be 0");
     594            1 :     tl_writer_free(&w);
     595              : }
     596              : 
     597            1 : void test_tl_writer_grow(void) {
     598              :     TlWriter w;
     599            1 :     tl_writer_init(&w);
     600              :     /* Write 1000 int32s — forces multiple reallocations */
     601         1001 :     for (int i = 0; i < 1000; i++) {
     602         1000 :         tl_write_int32(&w, i);
     603              :     }
     604            1 :     ASSERT(w.len == 4000, "1000 int32s should be 4000 bytes");
     605            1 :     TlReader r = tl_reader_init(w.data, w.len);
     606         1001 :     for (int i = 0; i < 1000; i++) {
     607         1000 :         ASSERT(tl_read_int32(&r) == i, "large round-trip mismatch");
     608              :     }
     609            1 :     tl_writer_free(&w);
     610              : }
     611              : 
     612              : /* ================================================================
     613              :  * Vector overflow / fuzz-style tests (TEST-30)
     614              :  *
     615              :  * Each test builds a TL vector header with count=0x7FFFFFFF followed by
     616              :  * only a few bytes of actual element data.  The saturating TlReader must
     617              :  * bound all reads to the buffer: no OOB access, no infinite spin.
     618              :  * ================================================================ */
     619              : 
     620              : /**
     621              :  * @brief Helper: build a buffer whose first 8 bytes are a TL vector header
     622              :  * (constructor 0x1cb5c415 + count) followed by @p extra bytes of payload.
     623              :  */
     624            4 : static void build_vector_buf(unsigned char *buf, size_t buf_len,
     625              :                               uint32_t count, const unsigned char *extra,
     626              :                               size_t extra_len)
     627              : {
     628              :     /* vector constructor LE */
     629            4 :     buf[0] = 0x15; buf[1] = 0xc4; buf[2] = 0xb5; buf[3] = 0x1c;
     630              :     /* count LE */
     631            4 :     buf[4] = (unsigned char)(count);
     632            4 :     buf[5] = (unsigned char)(count >> 8);
     633            4 :     buf[6] = (unsigned char)(count >> 16);
     634            4 :     buf[7] = (unsigned char)(count >> 24);
     635           20 :     for (size_t i = 0; i < extra_len && (8 + i) < buf_len; i++)
     636           16 :         buf[8 + i] = extra[i];
     637            4 : }
     638              : 
     639              : /**
     640              :  * @brief Vector with count=0x7FFFFFFF, only 4 bytes of uint32 payload.
     641              :  * Reading all claimed elements via tl_read_uint32 must not crash and
     642              :  * the reader must saturate after the real data runs out.
     643              :  */
     644            1 : void test_tl_vector_overflow_uint32(void) {
     645              :     /* vector header (8) + one real uint32 element (4) = 12 bytes total */
     646              :     unsigned char buf[12];
     647            1 :     unsigned char elem[4] = {0xAA, 0xBB, 0xCC, 0xDD};
     648            1 :     build_vector_buf(buf, sizeof(buf), 0x7FFFFFFFu, elem, 4);
     649              : 
     650            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     651              :     /* consume vector header */
     652            1 :     uint32_t ctor  = tl_read_uint32(&r);
     653            1 :     uint32_t count = tl_read_uint32(&r);
     654            1 :     ASSERT(ctor == 0x1cb5c415u, "vector ctor magic");
     655            1 :     ASSERT(count == 0x7FFFFFFFu, "count should be 0x7FFFFFFF");
     656              : 
     657              :     /* iterate — reader saturates after the first real element */
     658            1 :     uint32_t last_val = 0;
     659            1 :     for (uint32_t i = 0; i < count; i++) {
     660            1 :         last_val = tl_read_uint32(&r);
     661            1 :         if (!tl_reader_ok(&r)) break; /* natural termination */
     662              :     }
     663              :     /* The first element read the real bytes; subsequent reads returned 0 */
     664              :     (void)last_val;
     665            1 :     ASSERT(r.pos == r.len, "reader must be saturated at end of buffer");
     666              : }
     667              : 
     668              : /**
     669              :  * @brief Same but elements are int32 — loop terminates, no OOB.
     670              :  */
     671            1 : void test_tl_vector_overflow_int32(void) {
     672              :     unsigned char buf[12];
     673            1 :     unsigned char elem[4] = {0x01, 0x02, 0x03, 0x04};
     674            1 :     build_vector_buf(buf, sizeof(buf), 0x7FFFFFFFu, elem, 4);
     675              : 
     676            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     677            1 :     tl_read_uint32(&r); /* ctor */
     678            1 :     uint32_t count = tl_read_uint32(&r);
     679            1 :     ASSERT(count == 0x7FFFFFFFu, "count should be 0x7FFFFFFF");
     680              : 
     681            1 :     for (uint32_t i = 0; i < count; i++) {
     682            1 :         tl_read_int32(&r);
     683            1 :         if (!tl_reader_ok(&r)) break;
     684              :     }
     685            1 :     ASSERT(r.pos == r.len, "reader saturated after overflow int32 loop");
     686              : }
     687              : 
     688              : /**
     689              :  * @brief Vector whose claimed elements are uint64 (8 bytes each).
     690              :  * Only 8 bytes of real payload follow the header — reader saturates after one.
     691              :  */
     692            1 : void test_tl_vector_overflow_uint64(void) {
     693              :     unsigned char buf[16]; /* header(8) + one uint64(8) */
     694            1 :     unsigned char elem[8] = {1, 2, 3, 4, 5, 6, 7, 8};
     695            1 :     build_vector_buf(buf, sizeof(buf), 0x7FFFFFFFu, elem, 8);
     696              : 
     697            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     698            1 :     tl_read_uint32(&r); /* ctor */
     699            1 :     uint32_t count = tl_read_uint32(&r);
     700            1 :     ASSERT(count == 0x7FFFFFFFu, "count should be 0x7FFFFFFF");
     701              : 
     702            1 :     for (uint32_t i = 0; i < count; i++) {
     703            1 :         tl_read_uint64(&r);
     704            1 :         if (!tl_reader_ok(&r)) break;
     705              :     }
     706            1 :     ASSERT(r.pos == r.len, "reader saturated after overflow uint64 loop");
     707              : }
     708              : 
     709              : /**
     710              :  * @brief Vector whose elements are TL strings — only 4 bytes follow the header.
     711              :  * tl_read_string / tl_read_bytes must return NULL once the buffer is exhausted.
     712              :  */
     713            1 : void test_tl_vector_overflow_string(void) {
     714              :     /* header(8) + short string "hi" padded to 4 bytes = 12 bytes */
     715            1 :     unsigned char buf[12] = {
     716              :         0x15, 0xc4, 0xb5, 0x1c,  /* vector ctor */
     717              :         0xFF, 0xFF, 0xFF, 0x7F,  /* count = 0x7FFFFFFF LE */
     718              :         0x02, 'h',  'i',  0x00   /* TL string: len=2, "hi", 1 pad byte */
     719              :     };
     720              : 
     721            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     722            1 :     tl_read_uint32(&r); /* ctor */
     723            1 :     uint32_t count = tl_read_uint32(&r);
     724            1 :     ASSERT(count == 0x7FFFFFFFu, "count should be 0x7FFFFFFF");
     725              : 
     726            1 :     int null_seen = 0;
     727            1 :     for (uint32_t i = 0; i < count; i++) {
     728            1 :         char *s = tl_read_string(&r);
     729            1 :         if (s == NULL) { null_seen = 1; break; }
     730            1 :         free(s);
     731            1 :         if (!tl_reader_ok(&r)) { null_seen = 1; break; }
     732              :     }
     733            1 :     ASSERT(null_seen, "tl_read_string must return NULL when buffer exhausted");
     734            1 :     ASSERT(r.pos == r.len, "reader saturated after overflow string loop");
     735              : }
     736              : 
     737              : /**
     738              :  * @brief Minimal buffer: only the vector header, no element data.
     739              :  * The very first element read on a saturated reader must return 0 / NULL,
     740              :  * and pos must stay clamped to len throughout.
     741              :  */
     742            1 : void test_tl_vector_overflow_reader_saturates(void) {
     743              :     /* Only 8 bytes: the vector header, no element data at all */
     744              :     unsigned char buf[8];
     745            1 :     build_vector_buf(buf, sizeof(buf), 0x7FFFFFFFu, NULL, 0);
     746              : 
     747            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     748            1 :     tl_read_uint32(&r); /* ctor */
     749            1 :     uint32_t count = tl_read_uint32(&r);
     750            1 :     ASSERT(count == 0x7FFFFFFFu, "count should be 0x7FFFFFFF");
     751            1 :     ASSERT(r.pos == r.len, "reader exhausted after consuming header");
     752              : 
     753              :     /* Subsequent reads on exhausted reader must be safe */
     754            1 :     uint32_t v = tl_read_uint32(&r);
     755            1 :     ASSERT(v == 0, "read on exhausted reader returns 0");
     756            1 :     ASSERT(r.pos == r.len, "pos stays clamped at len");
     757              : 
     758            1 :     int64_t v64 = tl_read_int64(&r);
     759            1 :     ASSERT(v64 == 0, "int64 read on exhausted reader returns 0");
     760            1 :     ASSERT(r.pos == r.len, "pos still clamped");
     761              : }
     762              : 
     763              : /* ================================================================
     764              :  * TEST-36: tl_read_bytes giant / invalid length prefix tests
     765              :  * ================================================================ */
     766              : 
     767              : /**
     768              :  * @brief 4-byte buffer [0xFE, 0xFF, 0xFF, 0xFF] claims 16 MB of data.
     769              :  * The reader_has() guard must reject this without allocating 16 MB and
     770              :  * return NULL cleanly.
     771              :  */
     772            1 : void test_tl_read_bytes_giant_length_claim(void) {
     773            1 :     unsigned char buf[4] = {0xFE, 0xFF, 0xFF, 0xFF};
     774            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     775            1 :     size_t len = 0;
     776            1 :     unsigned char *b = tl_read_bytes(&r, &len);
     777            1 :     ASSERT(b == NULL, "giant length claim must return NULL");
     778            1 :     ASSERT(len == 0, "out_len must remain 0 on failure");
     779            1 :     ASSERT(r.pos == r.len, "reader must be saturated after failure");
     780              : }
     781              : 
     782              : /**
     783              :  * @brief Exact maximum valid long-form case: 4-byte header [0xFE, 0x00, 0x01, 0x00]
     784              :  * followed by exactly 256 real bytes (260 total, already 4-aligned).
     785              :  * Asserts correct length and content are returned.
     786              :  */
     787            1 : void test_tl_read_bytes_long_prefix_exact(void) {
     788            1 :     size_t total = 4 + 256; /* header + data, already 4-aligned: 260 */
     789            1 :     unsigned char *buf = (unsigned char *)calloc(1, total);
     790            1 :     buf[0] = 0xFE;
     791            1 :     buf[1] = 0x00; /* length LE: 0x000100 = 256 */
     792            1 :     buf[2] = 0x01;
     793            1 :     buf[3] = 0x00;
     794          257 :     for (size_t i = 0; i < 256; i++) buf[4 + i] = (unsigned char)(i & 0xFF);
     795              : 
     796            1 :     TlReader r = tl_reader_init(buf, total);
     797            1 :     size_t len = 0;
     798            1 :     unsigned char *b = tl_read_bytes(&r, &len);
     799            1 :     ASSERT(b != NULL, "exact 256-byte long prefix must succeed");
     800            1 :     ASSERT(len == 256, "length must be 256");
     801            1 :     int ok = 1;
     802          257 :     for (size_t i = 0; i < 256; i++) {
     803          256 :         if (b[i] != (unsigned char)(i & 0xFF)) { ok = 0; break; }
     804              :     }
     805            1 :     ASSERT(ok, "content must match the input bytes");
     806            1 :     ASSERT(r.pos == (size_t)total, "pos must advance to end of buffer");
     807            1 :     free(b);
     808            1 :     free(buf);
     809              : }
     810              : 
     811              : /**
     812              :  * @brief First byte 0xFF is an invalid TL bytes prefix (reserved/undefined).
     813              :  * The reader should treat it as a long-form prefix (>= 254) and then fail
     814              :  * cleanly because the claimed length will exceed the tiny buffer.
     815              :  */
     816            1 : void test_tl_read_bytes_invalid_ff_prefix(void) {
     817              :     /* 0xFF as first byte: the code treats first >= 254 as long form,
     818              :      * reads 3-byte LE length from bytes 1-3.  With a 4-byte buffer that
     819              :      * is all 0xFF, the claimed length is 0xFFFFFF = 16777215 bytes which
     820              :      * far exceeds the 4-byte buffer.  reader_has() must reject it. */
     821            1 :     unsigned char buf[4] = {0xFF, 0xFF, 0xFF, 0xFF};
     822            1 :     TlReader r = tl_reader_init(buf, sizeof(buf));
     823            1 :     size_t len = 0;
     824            1 :     unsigned char *b = tl_read_bytes(&r, &len);
     825            1 :     ASSERT(b == NULL, "0xFF prefix with insufficient buffer must return NULL");
     826            1 :     ASSERT(len == 0, "out_len must remain 0 on 0xFF prefix failure");
     827            1 :     ASSERT(r.pos == r.len, "reader must be saturated after 0xFF prefix failure");
     828              : }
     829              : 
     830              : /* ================================================================
     831              :  * TEST-42: Property-style roundtrip tests covering boundary lengths/values
     832              :  * ================================================================ */
     833              : 
     834              : /**
     835              :  * @brief Roundtrip tl_write_bytes / tl_read_bytes for boundary string lengths.
     836              :  *
     837              :  * Lengths tested: 0, 1, 253, 254, 1000, 100000 (proxy for 16 777 215).
     838              :  * Each buffer is filled with a repeating pattern; after a write+read the
     839              :  * content must match exactly.
     840              :  */
     841            1 : void test_tl_string_roundtrip_lengths(void) {
     842              :     /* Boundary lengths per the TL spec: <254 uses 1-byte prefix,
     843              :      * >=254 uses 4-byte prefix.  Also include a mid-range (1000) and a
     844              :      * cap of 100 000 bytes (instead of 16 777 215) to keep the test fast. */
     845            1 :     const size_t lengths[] = {0, 1, 253, 254, 1000, 100000};
     846            1 :     const int n = (int)(sizeof(lengths) / sizeof(lengths[0]));
     847              : 
     848            7 :     for (int li = 0; li < n; li++) {
     849            6 :         size_t slen = lengths[li];
     850              : 
     851              :         /* Build source data: repeating byte pattern 0x00..0xFF */
     852            6 :         unsigned char *src = NULL;
     853            6 :         if (slen > 0) {
     854            5 :             src = (unsigned char *)malloc(slen);
     855       101513 :             for (size_t i = 0; i < slen; i++)
     856       101508 :                 src[i] = (unsigned char)(i & 0xFF);
     857              :         }
     858              : 
     859              :         TlWriter w;
     860            6 :         tl_writer_init(&w);
     861            6 :         tl_write_bytes(&w, src, slen);
     862            6 :         ASSERT(w.len > 0, "writer must produce bytes for any length");
     863              : 
     864            6 :         TlReader r = tl_reader_init(w.data, w.len);
     865            6 :         size_t out_len = 0;
     866            6 :         unsigned char *out = tl_read_bytes(&r, &out_len);
     867              : 
     868            6 :         ASSERT(out != NULL, "tl_read_bytes must return non-NULL");
     869            6 :         ASSERT(out_len == slen, "read length must equal written length");
     870            6 :         if (slen > 0) {
     871            5 :             ASSERT(memcmp(out, src, slen) == 0, "roundtrip content must match");
     872              :         }
     873            6 :         ASSERT(r.pos == w.len, "reader must consume exactly the serialized bytes");
     874              : 
     875            6 :         free(out);
     876            6 :         free(src);
     877            6 :         tl_writer_free(&w);
     878              :     }
     879              : }
     880              : 
     881              : /**
     882              :  * @brief Roundtrip int32 across boundary values:
     883              :  * INT32_MIN, -1, 0, 1, INT32_MAX, and UINT32_MAX reinterpreted as int32.
     884              :  */
     885            1 : void test_tl_int32_roundtrip_boundaries(void) {
     886            1 :     int32_t values[] = {
     887              :         (int32_t)0x80000000,  /* INT32_MIN */
     888              :         -1,
     889              :         0,
     890              :         1,
     891              :         0x7FFFFFFF,           /* INT32_MAX */
     892              :         (int32_t)0xFFFFFFFF   /* -1 == UINT32_MAX cast to int32 */
     893              :     };
     894            1 :     const int n = (int)(sizeof(values) / sizeof(values[0]));
     895              : 
     896              :     TlWriter w;
     897            1 :     tl_writer_init(&w);
     898            7 :     for (int i = 0; i < n; i++)
     899            6 :         tl_write_int32(&w, values[i]);
     900              : 
     901            1 :     TlReader r = tl_reader_init(w.data, w.len);
     902            7 :     for (int i = 0; i < n; i++) {
     903            6 :         int32_t got = tl_read_int32(&r);
     904            6 :         ASSERT(got == values[i], "int32 boundary roundtrip mismatch");
     905              :     }
     906            1 :     ASSERT(!tl_reader_ok(&r), "reader must be at end after all int32 reads");
     907            1 :     tl_writer_free(&w);
     908              : }
     909              : 
     910              : /**
     911              :  * @brief Roundtrip int64 across boundary values:
     912              :  * INT64_MIN, -1, 0, 1, INT64_MAX.
     913              :  */
     914            1 : void test_tl_int64_roundtrip_boundaries(void) {
     915            1 :     int64_t values[] = {
     916              :         (int64_t)0x8000000000000000LL,  /* INT64_MIN */
     917              :         -1LL,
     918              :         0LL,
     919              :         1LL,
     920              :         0x7FFFFFFFFFFFFFFFLL            /* INT64_MAX */
     921              :     };
     922            1 :     const int n = (int)(sizeof(values) / sizeof(values[0]));
     923              : 
     924              :     TlWriter w;
     925            1 :     tl_writer_init(&w);
     926            6 :     for (int i = 0; i < n; i++)
     927            5 :         tl_write_int64(&w, values[i]);
     928              : 
     929            1 :     TlReader r = tl_reader_init(w.data, w.len);
     930            6 :     for (int i = 0; i < n; i++) {
     931            5 :         int64_t got = tl_read_int64(&r);
     932            5 :         ASSERT(got == values[i], "int64 boundary roundtrip mismatch");
     933              :     }
     934            1 :     ASSERT(!tl_reader_ok(&r), "reader must be at end after all int64 reads");
     935            1 :     tl_writer_free(&w);
     936              : }
     937              : 
     938              : /**
     939              :  * @brief Roundtrip TL vectors: empty (0 elements), single element, and many
     940              :  * elements (100).  Each vector is serialised via tl_write_vector_begin +
     941              :  * individual tl_write_int32 calls and then read back via tl_read_uint32 for
     942              :  * the header and tl_read_int32 for the elements.
     943              :  */
     944            1 : void test_tl_vector_roundtrip_empty_single_many(void) {
     945              :     /* --- empty vector --- */
     946              :     {
     947              :         TlWriter w;
     948            1 :         tl_writer_init(&w);
     949            1 :         tl_write_vector_begin(&w, 0);
     950              : 
     951            1 :         TlReader r = tl_reader_init(w.data, w.len);
     952            1 :         uint32_t ctor  = tl_read_uint32(&r);
     953            1 :         uint32_t count = tl_read_uint32(&r);
     954            1 :         ASSERT(ctor == 0x1cb5c415u, "vector ctor magic (empty)");
     955            1 :         ASSERT(count == 0, "empty vector count must be 0");
     956            1 :         ASSERT(!tl_reader_ok(&r), "reader exhausted after empty vector header");
     957            1 :         tl_writer_free(&w);
     958              :     }
     959              : 
     960              :     /* --- single-element vector --- */
     961              :     {
     962              :         TlWriter w;
     963            1 :         tl_writer_init(&w);
     964            1 :         tl_write_vector_begin(&w, 1);
     965            1 :         tl_write_int32(&w, 42);
     966              : 
     967            1 :         TlReader r = tl_reader_init(w.data, w.len);
     968            1 :         uint32_t ctor  = tl_read_uint32(&r);
     969            1 :         uint32_t count = tl_read_uint32(&r);
     970            1 :         ASSERT(ctor == 0x1cb5c415u, "vector ctor magic (single)");
     971            1 :         ASSERT(count == 1, "single-element vector count must be 1");
     972            1 :         int32_t val = tl_read_int32(&r);
     973            1 :         ASSERT(val == 42, "single-element roundtrip value mismatch");
     974            1 :         ASSERT(!tl_reader_ok(&r), "reader exhausted after single-element vector");
     975            1 :         tl_writer_free(&w);
     976              :     }
     977              : 
     978              :     /* --- many-element vector (100 elements) --- */
     979              :     {
     980            1 :         const uint32_t N = 100;
     981              :         TlWriter w;
     982            1 :         tl_writer_init(&w);
     983            1 :         tl_write_vector_begin(&w, N);
     984          101 :         for (uint32_t i = 0; i < N; i++)
     985          100 :             tl_write_int32(&w, (int32_t)i);
     986              : 
     987            1 :         TlReader r = tl_reader_init(w.data, w.len);
     988            1 :         uint32_t ctor  = tl_read_uint32(&r);
     989            1 :         uint32_t count = tl_read_uint32(&r);
     990            1 :         ASSERT(ctor == 0x1cb5c415u, "vector ctor magic (many)");
     991            1 :         ASSERT(count == N, "many-element vector count must be 100");
     992          101 :         for (uint32_t i = 0; i < N; i++) {
     993          100 :             int32_t val = tl_read_int32(&r);
     994          100 :             ASSERT(val == (int32_t)i, "many-element roundtrip value mismatch");
     995              :         }
     996            1 :         ASSERT(!tl_reader_ok(&r), "reader exhausted after many-element vector");
     997            1 :         tl_writer_free(&w);
     998              :     }
     999              : }
    1000              : 
    1001              : /* ================================================================
    1002              :  * Test suite entry point
    1003              :  * ================================================================ */
    1004              : 
    1005            1 : void test_tl_serial(void) {
    1006            1 :     RUN_TEST(test_tl_writer_init_free);
    1007            1 :     RUN_TEST(test_tl_write_int32);
    1008            1 :     RUN_TEST(test_tl_write_int32_negative);
    1009            1 :     RUN_TEST(test_tl_write_uint32);
    1010            1 :     RUN_TEST(test_tl_write_int64);
    1011            1 :     RUN_TEST(test_tl_write_uint64);
    1012            1 :     RUN_TEST(test_tl_write_int128);
    1013            1 :     RUN_TEST(test_tl_write_int256);
    1014            1 :     RUN_TEST(test_tl_write_double);
    1015            1 :     RUN_TEST(test_tl_write_bool_true);
    1016            1 :     RUN_TEST(test_tl_write_bool_false);
    1017            1 :     RUN_TEST(test_tl_write_string_short);
    1018            1 :     RUN_TEST(test_tl_write_string_empty);
    1019            1 :     RUN_TEST(test_tl_write_string_needs_padding);
    1020            1 :     RUN_TEST(test_tl_write_string_aligned);
    1021            1 :     RUN_TEST(test_tl_write_bytes_long);
    1022            1 :     RUN_TEST(test_tl_write_vector_begin);
    1023            1 :     RUN_TEST(test_tl_write_multiple);
    1024            1 :     RUN_TEST(test_tl_reader_init);
    1025            1 :     RUN_TEST(test_tl_reader_ok);
    1026            1 :     RUN_TEST(test_tl_read_int32);
    1027            1 :     RUN_TEST(test_tl_read_uint32);
    1028            1 :     RUN_TEST(test_tl_read_int64);
    1029            1 :     RUN_TEST(test_tl_read_int128);
    1030            1 :     RUN_TEST(test_tl_read_int256);
    1031            1 :     RUN_TEST(test_tl_read_bool_true);
    1032            1 :     RUN_TEST(test_tl_read_bool_false);
    1033            1 :     RUN_TEST(test_tl_read_bool_invalid);
    1034            1 :     RUN_TEST(test_tl_read_string_short);
    1035            1 :     RUN_TEST(test_tl_read_string_empty);
    1036            1 :     RUN_TEST(test_tl_read_bytes_with_padding);
    1037            1 :     RUN_TEST(test_tl_read_bytes_empty);
    1038            1 :     RUN_TEST(test_tl_read_bytes_long_prefix);
    1039            1 :     RUN_TEST(test_tl_read_past_end);
    1040            1 :     RUN_TEST(test_tl_read_skip);
    1041            1 :     RUN_TEST(test_tl_read_raw);
    1042            1 :     RUN_TEST(test_tl_roundtrip_int32);
    1043            1 :     RUN_TEST(test_tl_roundtrip_uint64);
    1044            1 :     RUN_TEST(test_tl_roundtrip_string);
    1045            1 :     RUN_TEST(test_tl_roundtrip_int128_int256);
    1046            1 :     RUN_TEST(test_tl_roundtrip_double);
    1047            1 :     RUN_TEST(test_tl_roundtrip_bool);
    1048            1 :     RUN_TEST(test_tl_roundtrip_mixed);
    1049            1 :     RUN_TEST(test_tl_string_null);
    1050            1 :     RUN_TEST(test_tl_writer_grow);
    1051            1 :     RUN_TEST(test_tl_vector_overflow_uint32);
    1052            1 :     RUN_TEST(test_tl_vector_overflow_int32);
    1053            1 :     RUN_TEST(test_tl_vector_overflow_uint64);
    1054            1 :     RUN_TEST(test_tl_vector_overflow_string);
    1055            1 :     RUN_TEST(test_tl_vector_overflow_reader_saturates);
    1056            1 :     RUN_TEST(test_tl_read_bytes_giant_length_claim);
    1057            1 :     RUN_TEST(test_tl_read_bytes_long_prefix_exact);
    1058            1 :     RUN_TEST(test_tl_read_bytes_invalid_ff_prefix);
    1059            1 :     RUN_TEST(test_tl_string_roundtrip_lengths);
    1060            1 :     RUN_TEST(test_tl_int32_roundtrip_boundaries);
    1061            1 :     RUN_TEST(test_tl_int64_roundtrip_boundaries);
    1062            1 :     RUN_TEST(test_tl_vector_roundtrip_empty_single_many);
    1063            1 : }
        

Generated by: LCOV version 2.0-1