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 : }
|