LCOV - code coverage report
Current view: top level - src/core - tl_serial.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 89.3 % 187 167
Test Date: 2026-04-20 19:54:24 Functions: 94.3 % 35 33

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file tl_serial.c
       6              :  * @brief TL (Type Language) binary serialization implementation.
       7              :  */
       8              : 
       9              : #include "tl_serial.h"
      10              : 
      11              : #include <stdio.h>
      12              : #include <stdlib.h>
      13              : #include <string.h>
      14              : 
      15              : /* ---- TL bool magic constants ---- */
      16              : #define TL_BOOL_TRUE  0x997275b5u
      17              : #define TL_BOOL_FALSE 0xbc799737u
      18              : #define TL_VECTOR_CTOR 0x1cb5c415u
      19              : 
      20              : /* ---- Writer helpers ---- */
      21              : 
      22        33809 : static void writer_ensure(TlWriter *w, size_t needed) {
      23        33809 :     if (w->len + needed <= w->cap) return;
      24         3617 :     size_t new_cap = w->cap ? w->cap : 64;
      25         6991 :     while (new_cap < w->len + needed) new_cap *= 2;
      26         3617 :     unsigned char *new_data = (unsigned char *)realloc(w->data, new_cap);
      27         3617 :     if (!new_data) {
      28            0 :         fprintf(stderr, "OOM: tl_writer realloc failed (%zu bytes)\n", new_cap);
      29            0 :         abort();
      30              :     }
      31         3617 :     w->data = new_data;
      32         3617 :     w->cap  = new_cap;
      33              : }
      34              : 
      35        33809 : static void writer_put(TlWriter *w, const void *src, size_t n) {
      36        33809 :     writer_ensure(w, n);
      37        33809 :     memcpy(w->data + w->len, src, n);
      38        33809 :     w->len += n;
      39        33809 : }
      40              : 
      41              : /* ---- Writer API ---- */
      42              : 
      43         2142 : void tl_writer_init(TlWriter *w) {
      44         2142 :     w->data = NULL;
      45         2142 :     w->len  = 0;
      46         2142 :     w->cap  = 0;
      47         2142 : }
      48              : 
      49         2142 : void tl_writer_free(TlWriter *w) {
      50         2142 :     free(w->data);
      51         2142 :     w->data = NULL;
      52         2142 :     w->len  = 0;
      53         2142 :     w->cap  = 0;
      54         2142 : }
      55              : 
      56         1651 : void tl_write_raw(TlWriter *w, const unsigned char *data, size_t len) {
      57         1651 :     writer_put(w, data, len);
      58         1651 : }
      59              : 
      60        18448 : static void write_le32(TlWriter *w, uint32_t val) {
      61              :     unsigned char buf[4];
      62        18448 :     buf[0] = (unsigned char)(val);
      63        18448 :     buf[1] = (unsigned char)(val >> 8);
      64        18448 :     buf[2] = (unsigned char)(val >> 16);
      65        18448 :     buf[3] = (unsigned char)(val >> 24);
      66        18448 :     writer_put(w, buf, 4);
      67        18448 : }
      68              : 
      69         4481 : static void write_le64(TlWriter *w, uint64_t val) {
      70              :     unsigned char buf[8];
      71        40329 :     for (int i = 0; i < 8; i++)
      72        35848 :         buf[i] = (unsigned char)(val >> (8 * i));
      73         4481 :     writer_put(w, buf, 8);
      74         4481 : }
      75              : 
      76         6696 : void tl_write_int32(TlWriter *w, int32_t val) {
      77         6696 :     write_le32(w, (uint32_t)val);
      78         6696 : }
      79              : 
      80        11752 : void tl_write_uint32(TlWriter *w, uint32_t val) {
      81        11752 :     write_le32(w, val);
      82        11752 : }
      83              : 
      84         2123 : void tl_write_int64(TlWriter *w, int64_t val) {
      85         2123 :     write_le64(w, (uint64_t)val);
      86         2123 : }
      87              : 
      88         2354 : void tl_write_uint64(TlWriter *w, uint64_t val) {
      89         2354 :     write_le64(w, val);
      90         2354 : }
      91              : 
      92           61 : void tl_write_int128(TlWriter *w, const unsigned char val[16]) {
      93           61 :     writer_put(w, val, 16);
      94           61 : }
      95              : 
      96            4 : void tl_write_int256(TlWriter *w, const unsigned char val[32]) {
      97            4 :     writer_put(w, val, 32);
      98            4 : }
      99              : 
     100            4 : void tl_write_double(TlWriter *w, double val) {
     101              :     /* IEEE 754 — reinterpret as uint64, write LE */
     102              :     uint64_t raw;
     103            4 :     memcpy(&raw, &val, sizeof(raw));
     104            4 :     write_le64(w, raw);
     105            4 : }
     106              : 
     107            0 : void tl_write_bool(TlWriter *w, int val) {
     108            0 :     write_le32(w, val ? TL_BOOL_TRUE : TL_BOOL_FALSE);
     109            0 : }
     110              : 
     111         3389 : void tl_write_string(TlWriter *w, const char *s) {
     112         3389 :     size_t len = s ? strlen(s) : 0;
     113         3389 :     tl_write_bytes(w, (const unsigned char *)s, len);
     114         3389 : }
     115              : 
     116         3558 : void tl_write_bytes(TlWriter *w, const unsigned char *data, size_t len) {
     117              :     /* Length prefix: if first byte < 254, use 1-byte prefix;
     118              :        otherwise 0xFE + 3-byte LE length */
     119         3558 :     if (len < 254) {
     120         3454 :         unsigned char prefix = (unsigned char)len;
     121         3454 :         writer_put(w, &prefix, 1);
     122              :     } else {
     123              :         unsigned char prefix[4];
     124          104 :         prefix[0] = 0xFE;
     125          104 :         prefix[1] = (unsigned char)(len);
     126          104 :         prefix[2] = (unsigned char)(len >> 8);
     127          104 :         prefix[3] = (unsigned char)(len >> 16);
     128          104 :         writer_put(w, prefix, 4);
     129              :     }
     130              : 
     131         3558 :     if (len > 0) writer_put(w, data, len);
     132              : 
     133              :     /* Padding to 4-byte boundary (including the length prefix byte(s)) */
     134         3558 :     size_t header = (len < 254) ? 1 : 4;
     135         3558 :     size_t total  = header + len;
     136         3558 :     size_t pad    = (4 - (total % 4)) % 4;
     137         3558 :     if (pad > 0) {
     138         3387 :         unsigned char zeros[3] = {0};
     139         3387 :         writer_put(w, zeros, pad);
     140              :     }
     141         3558 : }
     142              : 
     143            0 : void tl_write_vector_begin(TlWriter *w, uint32_t count) {
     144            0 :     write_le32(w, TL_VECTOR_CTOR);
     145            0 :     write_le32(w, count);
     146            0 : }
     147              : 
     148              : /* ---- Reader helpers ---- */
     149              : 
     150        25650 : static int reader_has(TlReader *r, size_t n) {
     151        25650 :     return r->pos + n <= r->len;
     152              : }
     153              : 
     154        15145 : static uint32_t read_le32(TlReader *r) {
     155        15145 :     if (!reader_has(r, 4)) { r->pos = r->len; return 0; }
     156        15145 :     uint32_t val = 0;
     157        15145 :     val |= (uint32_t)r->data[r->pos];
     158        15145 :     val |= (uint32_t)r->data[r->pos + 1] << 8;
     159        15145 :     val |= (uint32_t)r->data[r->pos + 2] << 16;
     160        15145 :     val |= (uint32_t)r->data[r->pos + 3] << 24;
     161        15145 :     r->pos += 4;
     162        15145 :     return val;
     163              : }
     164              : 
     165         3251 : static uint64_t read_le64(TlReader *r) {
     166         3251 :     if (!reader_has(r, 8)) { r->pos = r->len; return 0; }
     167         3251 :     uint64_t val = 0;
     168        29259 :     for (int i = 0; i < 8; i++)
     169        26008 :         val |= (uint64_t)r->data[r->pos + i] << (8 * i);
     170         3251 :     r->pos += 8;
     171         3251 :     return val;
     172              : }
     173              : 
     174              : /* ---- Reader API ---- */
     175              : 
     176         1367 : TlReader tl_reader_init(const unsigned char *data, size_t len) {
     177         1367 :     TlReader r = {data, len, 0};
     178         1367 :     return r;
     179              : }
     180              : 
     181         6040 : int tl_reader_ok(const TlReader *r) {
     182         6040 :     return r->pos < r->len;
     183              : }
     184              : 
     185         5373 : int32_t tl_read_int32(TlReader *r) {
     186         5373 :     return (int32_t)read_le32(r);
     187              : }
     188              : 
     189         9770 : uint32_t tl_read_uint32(TlReader *r) {
     190         9770 :     return read_le32(r);
     191              : }
     192              : 
     193         1861 : int64_t tl_read_int64(TlReader *r) {
     194         1861 :     return (int64_t)read_le64(r);
     195              : }
     196              : 
     197         1348 : uint64_t tl_read_uint64(TlReader *r) {
     198         1348 :     return read_le64(r);
     199              : }
     200              : 
     201           34 : void tl_read_int128(TlReader *r, unsigned char out[16]) {
     202           34 :     if (!reader_has(r, 16)) {
     203            0 :         memset(out, 0, 16);
     204            0 :         r->pos = r->len;
     205            0 :         return;
     206              :     }
     207           34 :     memcpy(out, r->data + r->pos, 16);
     208           34 :     r->pos += 16;
     209              : }
     210              : 
     211            1 : void tl_read_int256(TlReader *r, unsigned char out[32]) {
     212            1 :     if (!reader_has(r, 32)) {
     213            0 :         memset(out, 0, 32);
     214            0 :         r->pos = r->len;
     215            0 :         return;
     216              :     }
     217            1 :     memcpy(out, r->data + r->pos, 32);
     218            1 :     r->pos += 32;
     219              : }
     220              : 
     221           42 : double tl_read_double(TlReader *r) {
     222           42 :     uint64_t raw = read_le64(r);
     223           42 :     double val = 0.0;
     224           42 :     memcpy(&val, &raw, sizeof(val));
     225           42 :     return val;
     226              : }
     227              : 
     228            2 : int tl_read_bool(TlReader *r) {
     229            2 :     uint32_t magic = read_le32(r);
     230            2 :     if (magic == TL_BOOL_TRUE)  return 1;
     231            1 :     if (magic == TL_BOOL_FALSE) return 0;
     232            0 :     return -1; /* unrecognized */
     233              : }
     234              : 
     235         3175 : char *tl_read_string(TlReader *r) {
     236         3175 :     size_t len = 0;
     237         3175 :     unsigned char *bytes = tl_read_bytes(r, &len);
     238         3175 :     if (!bytes) return NULL;
     239              : 
     240              :     /* Null-terminate */
     241         3175 :     char *str = (char *)realloc(bytes, len + 1);
     242         3175 :     if (!str) { free(bytes); return NULL; }
     243         3175 :     str[len] = '\0';
     244         3175 :     return str;
     245              : }
     246              : 
     247         3249 : unsigned char *tl_read_bytes(TlReader *r, size_t *out_len) {
     248         3249 :     *out_len = 0;
     249              : 
     250              :     /* Read length prefix */
     251         3249 :     if (!reader_has(r, 1)) { r->pos = r->len; return NULL; }
     252              : 
     253              :     size_t header_size;
     254              :     size_t data_len;
     255              : 
     256         3249 :     unsigned char first = r->data[r->pos];
     257         3249 :     if (first < 254) {
     258         3184 :         data_len    = first;
     259         3184 :         header_size = 1;
     260              :     } else {
     261           65 :         if (!reader_has(r, 4)) { r->pos = r->len; return NULL; }
     262           65 :         data_len  = (size_t)r->data[r->pos + 1]
     263           65 :                   | ((size_t)r->data[r->pos + 2] << 8)
     264           65 :                   | ((size_t)r->data[r->pos + 3] << 16);
     265           65 :         header_size = 4;
     266              :     }
     267              : 
     268              :     /* Check we have enough data */
     269         3249 :     size_t total_raw = header_size + data_len;
     270         3249 :     if (!reader_has(r, total_raw)) { r->pos = r->len; return NULL; }
     271              : 
     272              :     /* Allocate and copy data.
     273              :      * C11 malloc(0) is implementation-defined and may return NULL; callers
     274              :      * treat NULL as allocation failure, so we always request at least 1 byte
     275              :      * to guarantee a non-NULL return for valid zero-length TL bytes/strings. */
     276         3249 :     unsigned char *result = (unsigned char *)malloc(data_len ? data_len : 1);
     277         3249 :     if (!result) return NULL;
     278         3249 :     if (data_len > 0) memcpy(result, r->data + r->pos + header_size, data_len);
     279              : 
     280              :     /* Skip padding */
     281         3249 :     size_t padded = (total_raw + 3) & ~(size_t)3;
     282         3249 :     r->pos += padded;
     283              : 
     284         3249 :     *out_len = data_len;
     285         3249 :     return result;
     286              : }
     287              : 
     288          656 : void tl_read_raw(TlReader *r, unsigned char *out, size_t len) {
     289          656 :     if (!reader_has(r, len)) {
     290            0 :         memset(out, 0, len);
     291            0 :         r->pos = r->len;
     292            0 :         return;
     293              :     }
     294          656 :     memcpy(out, r->data + r->pos, len);
     295          656 :     r->pos += len;
     296              : }
     297              : 
     298            7 : void tl_read_skip(TlReader *r, size_t len) {
     299            7 :     if (r->pos + len > r->len) {
     300            0 :         r->pos = r->len;
     301              :     } else {
     302            7 :         r->pos += len;
     303              :     }
     304            7 : }
        

Generated by: LCOV version 2.0-1