LCOV - code coverage report
Current view: top level - src/core - tl_serial.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 94.1 % 187 176
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 35 35

            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        78173 : static void writer_ensure(TlWriter *w, size_t needed) {
      23        78173 :     if (w->len + needed <= w->cap) return;
      24         9056 :     size_t new_cap = w->cap ? w->cap : 64;
      25        18042 :     while (new_cap < w->len + needed) new_cap *= 2;
      26         9056 :     unsigned char *new_data = (unsigned char *)realloc(w->data, new_cap);
      27         9056 :     if (!new_data) {
      28            0 :         fprintf(stderr, "OOM: tl_writer realloc failed (%zu bytes)\n", new_cap);
      29            0 :         abort();
      30              :     }
      31         9056 :     w->data = new_data;
      32         9056 :     w->cap  = new_cap;
      33              : }
      34              : 
      35        78173 : static void writer_put(TlWriter *w, const void *src, size_t n) {
      36        78173 :     writer_ensure(w, n);
      37        78173 :     memcpy(w->data + w->len, src, n);
      38        78173 :     w->len += n;
      39        78173 : }
      40              : 
      41              : /* ---- Writer API ---- */
      42              : 
      43         5377 : void tl_writer_init(TlWriter *w) {
      44         5377 :     w->data = NULL;
      45         5377 :     w->len  = 0;
      46         5377 :     w->cap  = 0;
      47         5377 : }
      48              : 
      49         5377 : void tl_writer_free(TlWriter *w) {
      50         5377 :     free(w->data);
      51         5377 :     w->data = NULL;
      52         5377 :     w->len  = 0;
      53         5377 :     w->cap  = 0;
      54         5377 : }
      55              : 
      56         4413 : void tl_write_raw(TlWriter *w, const unsigned char *data, size_t len) {
      57         4413 :     writer_put(w, data, len);
      58         4413 : }
      59              : 
      60        41517 : static void write_le32(TlWriter *w, uint32_t val) {
      61              :     unsigned char buf[4];
      62        41517 :     buf[0] = (unsigned char)(val);
      63        41517 :     buf[1] = (unsigned char)(val >> 8);
      64        41517 :     buf[2] = (unsigned char)(val >> 16);
      65        41517 :     buf[3] = (unsigned char)(val >> 24);
      66        41517 :     writer_put(w, buf, 4);
      67        41517 : }
      68              : 
      69         9979 : static void write_le64(TlWriter *w, uint64_t val) {
      70              :     unsigned char buf[8];
      71        89811 :     for (int i = 0; i < 8; i++)
      72        79832 :         buf[i] = (unsigned char)(val >> (8 * i));
      73         9979 :     writer_put(w, buf, 8);
      74         9979 : }
      75              : 
      76        15648 : void tl_write_int32(TlWriter *w, int32_t val) {
      77        15648 :     write_le32(w, (uint32_t)val);
      78        15648 : }
      79              : 
      80        25848 : void tl_write_uint32(TlWriter *w, uint32_t val) {
      81        25848 :     write_le32(w, val);
      82        25848 : }
      83              : 
      84         4558 : void tl_write_int64(TlWriter *w, int64_t val) {
      85         4558 :     write_le64(w, (uint64_t)val);
      86         4558 : }
      87              : 
      88         5399 : void tl_write_uint64(TlWriter *w, uint64_t val) {
      89         5399 :     write_le64(w, val);
      90         5399 : }
      91              : 
      92          238 : void tl_write_int128(TlWriter *w, const unsigned char val[16]) {
      93          238 :     writer_put(w, val, 16);
      94          238 : }
      95              : 
      96           13 : void tl_write_int256(TlWriter *w, const unsigned char val[32]) {
      97           13 :     writer_put(w, val, 32);
      98           13 : }
      99              : 
     100           22 : void tl_write_double(TlWriter *w, double val) {
     101              :     /* IEEE 754 — reinterpret as uint64, write LE */
     102              :     uint64_t raw;
     103           22 :     memcpy(&raw, &val, sizeof(raw));
     104           22 :     write_le64(w, raw);
     105           22 : }
     106              : 
     107           13 : void tl_write_bool(TlWriter *w, int val) {
     108           13 :     write_le32(w, val ? TL_BOOL_TRUE : TL_BOOL_FALSE);
     109           13 : }
     110              : 
     111         7950 : void tl_write_string(TlWriter *w, const char *s) {
     112         7950 :     size_t len = s ? strlen(s) : 0;
     113         7950 :     tl_write_bytes(w, (const unsigned char *)s, len);
     114         7950 : }
     115              : 
     116         8426 : 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         8426 :     if (len < 254) {
     120         8179 :         unsigned char prefix = (unsigned char)len;
     121         8179 :         writer_put(w, &prefix, 1);
     122              :     } else {
     123              :         unsigned char prefix[4];
     124          247 :         prefix[0] = 0xFE;
     125          247 :         prefix[1] = (unsigned char)(len);
     126          247 :         prefix[2] = (unsigned char)(len >> 8);
     127          247 :         prefix[3] = (unsigned char)(len >> 16);
     128          247 :         writer_put(w, prefix, 4);
     129              :     }
     130              : 
     131         8426 :     if (len > 0) writer_put(w, data, len);
     132              : 
     133              :     /* Padding to 4-byte boundary (including the length prefix byte(s)) */
     134         8426 :     size_t header = (len < 254) ? 1 : 4;
     135         8426 :     size_t total  = header + len;
     136         8426 :     size_t pad    = (4 - (total % 4)) % 4;
     137         8426 :     if (pad > 0) {
     138         7995 :         unsigned char zeros[3] = {0};
     139         7995 :         writer_put(w, zeros, pad);
     140              :     }
     141         8426 : }
     142              : 
     143            4 : void tl_write_vector_begin(TlWriter *w, uint32_t count) {
     144            4 :     write_le32(w, TL_VECTOR_CTOR);
     145            4 :     write_le32(w, count);
     146            4 : }
     147              : 
     148              : /* ---- Reader helpers ---- */
     149              : 
     150        56324 : static int reader_has(TlReader *r, size_t n) {
     151        56324 :     return r->pos + n <= r->len;
     152              : }
     153              : 
     154        33470 : static uint32_t read_le32(TlReader *r) {
     155        33470 :     if (!reader_has(r, 4)) { r->pos = r->len; return 0; }
     156        33468 :     uint32_t val = 0;
     157        33468 :     val |= (uint32_t)r->data[r->pos];
     158        33468 :     val |= (uint32_t)r->data[r->pos + 1] << 8;
     159        33468 :     val |= (uint32_t)r->data[r->pos + 2] << 16;
     160        33468 :     val |= (uint32_t)r->data[r->pos + 3] << 24;
     161        33468 :     r->pos += 4;
     162        33468 :     return val;
     163              : }
     164              : 
     165         7314 : static uint64_t read_le64(TlReader *r) {
     166         7314 :     if (!reader_has(r, 8)) { r->pos = r->len; return 0; }
     167         7313 :     uint64_t val = 0;
     168        65817 :     for (int i = 0; i < 8; i++)
     169        58504 :         val |= (uint64_t)r->data[r->pos + i] << (8 * i);
     170         7313 :     r->pos += 8;
     171         7313 :     return val;
     172              : }
     173              : 
     174              : /* ---- Reader API ---- */
     175              : 
     176         3324 : TlReader tl_reader_init(const unsigned char *data, size_t len) {
     177         3324 :     TlReader r = {data, len, 0};
     178         3324 :     return r;
     179              : }
     180              : 
     181        12793 : int tl_reader_ok(const TlReader *r) {
     182        12793 :     return r->pos < r->len;
     183              : }
     184              : 
     185        12298 : int32_t tl_read_int32(TlReader *r) {
     186        12298 :     return (int32_t)read_le32(r);
     187              : }
     188              : 
     189        21158 : uint32_t tl_read_uint32(TlReader *r) {
     190        21158 :     return read_le32(r);
     191              : }
     192              : 
     193         3908 : int64_t tl_read_int64(TlReader *r) {
     194         3908 :     return (int64_t)read_le64(r);
     195              : }
     196              : 
     197         3308 : uint64_t tl_read_uint64(TlReader *r) {
     198         3308 :     return read_le64(r);
     199              : }
     200              : 
     201          123 : void tl_read_int128(TlReader *r, unsigned char out[16]) {
     202          123 :     if (!reader_has(r, 16)) {
     203            0 :         memset(out, 0, 16);
     204            0 :         r->pos = r->len;
     205            0 :         return;
     206              :     }
     207          123 :     memcpy(out, r->data + r->pos, 16);
     208          123 :     r->pos += 16;
     209              : }
     210              : 
     211            4 : void tl_read_int256(TlReader *r, unsigned char out[32]) {
     212            4 :     if (!reader_has(r, 32)) {
     213            0 :         memset(out, 0, 32);
     214            0 :         r->pos = r->len;
     215            0 :         return;
     216              :     }
     217            4 :     memcpy(out, r->data + r->pos, 32);
     218            4 :     r->pos += 32;
     219              : }
     220              : 
     221           98 : double tl_read_double(TlReader *r) {
     222           98 :     uint64_t raw = read_le64(r);
     223           98 :     double val = 0.0;
     224           98 :     memcpy(&val, &raw, sizeof(val));
     225           98 :     return val;
     226              : }
     227              : 
     228           14 : int tl_read_bool(TlReader *r) {
     229           14 :     uint32_t magic = read_le32(r);
     230           14 :     if (magic == TL_BOOL_TRUE)  return 1;
     231            6 :     if (magic == TL_BOOL_FALSE) return 0;
     232            1 :     return -1; /* unrecognized */
     233              : }
     234              : 
     235         6624 : char *tl_read_string(TlReader *r) {
     236         6624 :     size_t len = 0;
     237         6624 :     unsigned char *bytes = tl_read_bytes(r, &len);
     238         6624 :     if (!bytes) return NULL;
     239              : 
     240              :     /* Null-terminate */
     241         6624 :     char *str = (char *)realloc(bytes, len + 1);
     242         6624 :     if (!str) { free(bytes); return NULL; }
     243         6624 :     str[len] = '\0';
     244         6624 :     return str;
     245              : }
     246              : 
     247         6831 : unsigned char *tl_read_bytes(TlReader *r, size_t *out_len) {
     248         6831 :     *out_len = 0;
     249              : 
     250              :     /* Read length prefix */
     251         6831 :     if (!reader_has(r, 1)) { r->pos = r->len; return NULL; }
     252              : 
     253              :     size_t header_size;
     254              :     size_t data_len;
     255              : 
     256         6831 :     unsigned char first = r->data[r->pos];
     257         6831 :     if (first < 254) {
     258         6691 :         data_len    = first;
     259         6691 :         header_size = 1;
     260              :     } else {
     261          140 :         if (!reader_has(r, 4)) { r->pos = r->len; return NULL; }
     262          140 :         data_len  = (size_t)r->data[r->pos + 1]
     263          140 :                   | ((size_t)r->data[r->pos + 2] << 8)
     264          140 :                   | ((size_t)r->data[r->pos + 3] << 16);
     265          140 :         header_size = 4;
     266              :     }
     267              : 
     268              :     /* Check we have enough data */
     269         6831 :     size_t total_raw = header_size + data_len;
     270         6831 :     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         6829 :     unsigned char *result = (unsigned char *)malloc(data_len ? data_len : 1);
     277         6829 :     if (!result) return NULL;
     278         6829 :     if (data_len > 0) memcpy(result, r->data + r->pos + header_size, data_len);
     279              : 
     280              :     /* Skip padding */
     281         6829 :     size_t padded = (total_raw + 3) & ~(size_t)3;
     282         6829 :     r->pos += padded;
     283              : 
     284         6829 :     *out_len = data_len;
     285         6829 :     return result;
     286              : }
     287              : 
     288         1611 : void tl_read_raw(TlReader *r, unsigned char *out, size_t len) {
     289         1611 :     if (!reader_has(r, len)) {
     290            0 :         memset(out, 0, len);
     291            0 :         r->pos = r->len;
     292            0 :         return;
     293              :     }
     294         1611 :     memcpy(out, r->data + r->pos, len);
     295         1611 :     r->pos += len;
     296              : }
     297              : 
     298           20 : void tl_read_skip(TlReader *r, size_t len) {
     299           20 :     if (r->pos + len > r->len) {
     300            1 :         r->pos = r->len;
     301              :     } else {
     302           19 :         r->pos += len;
     303              :     }
     304           20 : }
        

Generated by: LCOV version 2.0-1