• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TEN-framework / ten-framework / 20358357829

19 Dec 2025 03:00AM UTC coverage: 57.587% (+0.2%) from 57.34%
20358357829

push

github

web-flow
fix: coveralls remove container for all test jobs (#1878)

* chore: calculate coverage in debug mode

* Revert "chore: calculate coverage in debug mode"

This reverts commit f68a031af.

* Reapply "chore: calculate coverage in debug mode"

This reverts commit bc27f2c1c.

* fix: remove container for test jobs

* fix: add C++ toolchain, go builder, sanitizer support, nodejs env

* fix: compiler version incompatible

* Revert "fix: compiler version incompatible"

This reverts commit 23f5c3f7d.

* fix: set PATH so that clang-21 is find earlier than older versions

* fix: uninstall older versions of clang

* Revert "fix: set PATH so that clang-21 is find earlier than older versions"

This reverts commit b66e56c08.

* fix: uninstall old clang first then install clang21

* fix: supplement test jobs deps according to tools Dockerfile

* fix: install libasan5 for all jobs and remove unecessary deps

* chore: refine codes and output clang version before every test

* fix: go back to clang 18 to detect __lsan_do_leak_check

* Revert "fix: go back to clang 18 to detect __lsan_do_leak_check"

This reverts commit d247818a8.

* fix: upgrade libasan5 to libasan8 to match clang 21

* fix: install libclang-rt-21-dev

* Revert "fix: install libclang-rt-21-dev"

This reverts commit 39b96e030.

* fix: ten_enable_go_app_leak_check

* chore: uninstall useless dep libasan8

* chore: refine codes and uninstall unecessary dep clang-tool-21

54480 of 94605 relevant lines covered (57.59%)

682622.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.88
/core/src/ten_utils/lib/sys/general/string.c
1
//
2
// Copyright © 2025 Agora
3
// This file is part of TEN Framework, an open source project.
4
// Licensed under the Apache License, Version 2.0, with certain conditions.
5
// Refer to the "LICENSE" file in the root directory for more information.
6
//
7
#include "ten_utils/lib/string.h"
8

9
#include <ctype.h>
10
#include <stdarg.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14

15
#include "include_internal/ten_utils/lib/safe_cast.h"
16
#include "ten_utils/container/list.h"
17
#include "ten_utils/lib/alloc.h"
18
#include "ten_utils/lib/signature.h"
19
#include "ten_utils/macro/check.h"
20
#include "ten_utils/macro/memory.h"
21

22
#if !defined(OS_WINDOWS)
23
#include <unistd.h>
24
#endif
25

26
#if defined(_WIN32) || defined(_WIN64)
27
#define strtok_r strtok_s
28
#endif
29

30
ten_string_t *ten_string_create(void) {
36,531✔
31
  ten_string_t *self = (ten_string_t *)TEN_MALLOC(sizeof(ten_string_t));
36,531✔
32
  TEN_ASSERT(self, "Failed to allocate memory.");
36,531✔
33

34
  TEN_STRING_INIT(*self);
36,531✔
35

36
  return self;
36,531✔
37
}
36,531✔
38

39
ten_string_t *ten_string_create_from_c_str_with_size(const char *str,
40
                                                     size_t size) {
×
41
  TEN_ASSERT(str, "Invalid argument.");
×
42

43
  ten_string_t *result = ten_string_create();
×
44
  ten_string_set_from_c_str_with_size(result, str, size);
×
45

46
  return result;
×
47
}
×
48

49
ten_string_t *ten_string_create_from_c_str(const char *str) {
23✔
50
  TEN_ASSERT(str, "Invalid argument.");
23✔
51

52
  ten_string_t *result = ten_string_create();
23✔
53
  ten_string_set_from_c_str(result, str);
23✔
54

55
  return result;
23✔
56
}
23✔
57

58
enum { MAX_RETRIES = 10 };
59

60
void ten_string_append_from_va_list(ten_string_t *self, const char *fmt,
61
                                    va_list ap) {
2,400,092✔
62
  TEN_ASSERT(self, "Invalid argument.");
2,400,092✔
63
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
2,400,092✔
64

65
  size_t retry_count = 0;
2,400,092✔
66

67
  va_list cp;
2,400,092✔
68
  va_copy(cp, ap);
2,400,092✔
69

70
  for (;;) {
3,945,077✔
71
    va_list temp;
3,945,077✔
72
    va_copy(temp, cp);
3,945,077✔
73

74
    int n = vsnprintf(&self->buf[self->first_unused_idx],
3,945,077✔
75
                      self->buf_size - self->first_unused_idx, fmt, temp);
3,945,077✔
76

77
    va_end(temp);
3,945,077✔
78

79
    if ((n > -1) && ((size_t)n < (self->buf_size - self->first_unused_idx))) {
3,945,077✔
80
      self->first_unused_idx += n;
2,400,092✔
81
      va_end(cp);
2,400,092✔
82
      return;
2,400,092✔
83
    }
2,400,092✔
84

85
    // Else try again with more space.
86
    if (n > -1) {
1,544,985✔
87
      ten_string_reserve(self, n + 1);  // Exact
1,544,985✔
88
    } else {
1,544,985✔
89
      ten_string_reserve(self, self->buf_size * 2);  // 2x
×
90
    }
×
91

92
    if (++retry_count > MAX_RETRIES) {
1,544,985✔
93
      va_end(cp);
×
94

95
      TEN_ASSERT(0, "Should not happen");
×
96
      return;
×
97
    }
×
98
  }
1,544,985✔
99
}
2,400,092✔
100

101
ten_string_t *ten_string_create_formatted(const char *fmt, ...) {
36,498✔
102
  ten_string_t *self = ten_string_create();
36,498✔
103
  va_list ap;
36,498✔
104
  va_start(ap, fmt);
36,498✔
105
  ten_string_append_from_va_list(self, fmt, ap);
36,498✔
106
  va_end(ap);
36,498✔
107
  return self;
36,498✔
108
}
36,498✔
109

110
ten_string_t *ten_string_create_from_va_list(const char *fmt, va_list ap) {
9✔
111
  ten_string_t *self = ten_string_create();
9✔
112
  ten_string_append_from_va_list(self, fmt, ap);
9✔
113
  return self;
9✔
114
}
9✔
115

116
ten_string_t *ten_string_clone(const ten_string_t *other) {
11,673✔
117
  TEN_ASSERT(other && ten_string_check_integrity(other), "Invalid argument.");
11,673✔
118

119
  return ten_string_create_formatted("%s", ten_string_get_raw_str(other));
11,673✔
120
}
11,673✔
121

122
void ten_string_destroy(ten_string_t *self) {
36,531✔
123
  TEN_ASSERT(self, "Invalid argument.");
36,531✔
124
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
36,531✔
125
  ten_string_deinit(self);
36,531✔
126
  TEN_FREE(self);
36,531✔
127
}
36,531✔
128

129
void ten_string_init(ten_string_t *self) {
18,627✔
130
  TEN_ASSERT(self, "Invalid argument.");
18,627✔
131
  // TEN_ASSERT(ten_signature_get(&self->signature) != TEN_STRING_SIGNATURE,
132
  //            "Should not happen.");
133

134
  ten_signature_set(&self->signature, (ten_signature_t)TEN_STRING_SIGNATURE);
18,627✔
135

136
  self->buf = self->pre_buf;
18,627✔
137
  self->buf_size = TEN_STRING_PRE_BUF_SIZE;
18,627✔
138
  self->first_unused_idx = 0;
18,627✔
139
  self->buf[0] = 0;
18,627✔
140
}
18,627✔
141

142
void ten_string_init_from_va_list(ten_string_t *self, const char *fmt,
143
                                  va_list ap) {
616,184✔
144
  TEN_ASSERT(self, "Invalid argument.");
616,184✔
145

146
  TEN_STRING_INIT(*self);
616,184✔
147

148
  ten_string_append_from_va_list(self, fmt, ap);
616,184✔
149
}
616,184✔
150

151
void ten_string_init_from_string(ten_string_t *self, ten_string_t *other) {
94,910✔
152
  TEN_ASSERT(self, "Invalid argument.");
94,910✔
153
  TEN_ASSERT(other && ten_string_check_integrity(other) &&
94,910✔
154
                 ten_string_get_raw_str(other),
94,910✔
155
             "Invalid argument.");
94,910✔
156

157
  ten_string_init_formatted(self, "%s", ten_string_get_raw_str(other));
94,910✔
158
}
94,910✔
159

160
void ten_string_copy(ten_string_t *self, ten_string_t *other) {
8,631✔
161
  TEN_ASSERT(self, "Invalid argument.");
8,631✔
162
  TEN_ASSERT(other && ten_string_check_integrity(other) &&
8,631✔
163
                 ten_string_get_raw_str(other),
8,631✔
164
             "Invalid argument.");
8,631✔
165

166
  ten_string_set_formatted(self, "%s", ten_string_get_raw_str(other));
8,631✔
167
}
8,631✔
168

169
void ten_string_init_formatted(ten_string_t *self, const char *fmt, ...) {
347,357✔
170
  TEN_ASSERT(self, "Invalid argument.");
347,357✔
171

172
  va_list ap;
347,357✔
173
  va_start(ap, fmt);
347,357✔
174
  ten_string_init_from_va_list(self, fmt, ap);
347,357✔
175
  va_end(ap);
347,357✔
176
}
347,357✔
177

178
void ten_string_init_from_c_str_with_size(ten_string_t *self, const char *str,
179
                                          size_t size) {
75,232✔
180
  TEN_ASSERT(self, "Invalid argument.");
75,232✔
181

182
  TEN_STRING_INIT(*self);
75,232✔
183

184
  if (str) {
75,232✔
185
    int int_size = 0;
72,286✔
186
    bool rc = safe_cast_size_t_to_int(size, &int_size);
72,286✔
187
    TEN_ASSERT(rc, "Size overflow detected.");
72,286✔
188

189
    ten_string_set_formatted(self, "%.*s", int_size, str);
72,286✔
190
  }
72,286✔
191
}
75,232✔
192

193
void ten_string_set_from_c_str_with_size(ten_string_t *self, const char *str,
194
                                         size_t size) {
367,176✔
195
  TEN_ASSERT(self, "Invalid argument.");
367,176✔
196
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
367,176✔
197

198
  if (str) {
367,176✔
199
    int int_size = 0;
367,176✔
200
    bool rc = safe_cast_size_t_to_int(size, &int_size);
367,176✔
201
    TEN_ASSERT(rc, "Size overflow detected.");
367,176✔
202

203
    ten_string_set_formatted(self, "%.*s", int_size, str);
367,176✔
204
  }
367,176✔
205
}
367,176✔
206

207
void ten_string_set_from_c_str(ten_string_t *self, const char *str) {
39,026✔
208
  TEN_ASSERT(self, "Invalid argument.");
39,026✔
209
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
39,026✔
210
  TEN_ASSERT(str, "Invalid argument.");
39,026✔
211

212
  ten_string_set_formatted(self, "%s", str);
39,026✔
213
}
39,026✔
214

215
void ten_string_set_formatted(ten_string_t *self, const char *fmt, ...) {
1,015,104✔
216
  TEN_ASSERT(self, "Invalid argument.");
1,015,104✔
217
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
1,015,104✔
218

219
  ten_string_clear(self);
1,015,104✔
220

221
  va_list ap;
1,015,104✔
222
  va_start(ap, fmt);
1,015,104✔
223
  ten_string_append_from_va_list(self, fmt, ap);
1,015,104✔
224
  va_end(ap);
1,015,104✔
225
}
1,015,104✔
226

227
void ten_string_prepend_from_va_list(ten_string_t *self, const char *fmt,
228
                                     va_list ap) {
57✔
229
  TEN_ASSERT(self, "Invalid argument.");
57✔
230
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
57✔
231
  TEN_ASSERT(fmt, "Invalid argument.");
57✔
232

233
  ten_string_t new_str;
57✔
234
  TEN_STRING_INIT(new_str);
57✔
235

236
  ten_string_append_from_va_list(&new_str, fmt, ap);
57✔
237
  ten_string_append_formatted(&new_str, "%s", ten_string_get_raw_str(self));
57✔
238

239
  // Move the content of new_str to self.
240
  self->buf_size = new_str.buf_size;
57✔
241
  self->first_unused_idx = new_str.first_unused_idx;
57✔
242
  if (new_str.buf == new_str.pre_buf) {
57✔
243
    strcpy(self->pre_buf, new_str.pre_buf);
30✔
244
  } else {
30✔
245
    if (self->buf && self->buf != self->pre_buf) {
27✔
246
      TEN_FREE(self->buf);
24✔
247
    }
24✔
248

249
    self->buf = new_str.buf;
27✔
250
    new_str.buf = new_str.pre_buf;
27✔
251
  }
27✔
252

253
  ten_string_deinit(&new_str);
57✔
254
}
57✔
255

256
void ten_string_prepend_formatted(ten_string_t *self, const char *fmt, ...) {
34✔
257
  TEN_ASSERT(self, "Invalid argument.");
34✔
258
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
34✔
259
  TEN_ASSERT(fmt, "Invalid argument.");
34✔
260

261
  va_list ap;
34✔
262
  va_start(ap, fmt);
34✔
263
  ten_string_prepend_from_va_list(self, fmt, ap);
34✔
264
  va_end(ap);
34✔
265
}
34✔
266

267
void ten_string_append_formatted(ten_string_t *self, const char *fmt, ...) {
491,985✔
268
  TEN_ASSERT(self, "Invalid argument.");
491,985✔
269
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
491,985✔
270
  TEN_ASSERT(fmt, "Invalid argument.");
491,985✔
271

272
  va_list ap;
491,985✔
273
  va_start(ap, fmt);
491,985✔
274
  ten_string_append_from_va_list(self, fmt, ap);
491,985✔
275
  va_end(ap);
491,985✔
276
}
491,985✔
277

278
static void ten_string_reset(ten_string_t *self) {
3,844,782✔
279
  TEN_ASSERT(self, "Invalid argument.");
3,844,782✔
280
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
3,844,782✔
281

282
  if (self->buf && self->buf != self->pre_buf) {
3,844,782✔
283
    TEN_FREE(self->buf);
1,545,244✔
284
    self->buf = self->pre_buf;
1,545,244✔
285
  }
1,545,244✔
286
  self->buf_size = TEN_STRING_PRE_BUF_SIZE;
3,844,782✔
287
  self->first_unused_idx = 0;
3,844,782✔
288
}
3,844,782✔
289

290
void ten_string_deinit(ten_string_t *self) {
3,844,782✔
291
  TEN_ASSERT(self, "Invalid argument.");
3,844,782✔
292
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
3,844,782✔
293

294
  ten_string_reset(self);
3,844,782✔
295
  ten_signature_set(&self->signature, 0);
3,844,782✔
296
}
3,844,782✔
297

298
void ten_string_clear(ten_string_t *self) {
1,391,367✔
299
  TEN_ASSERT(self, "Invalid argument.");
1,391,367✔
300
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
1,391,367✔
301
  self->first_unused_idx = 0;
1,391,367✔
302
  self->buf[0] = 0;
1,391,367✔
303
}
1,391,367✔
304

305
void ten_string_reserve(ten_string_t *self, size_t extra) {
1,545,534✔
306
  TEN_ASSERT(self, "Invalid argument.");
1,545,534✔
307
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
1,545,534✔
308

309
  if (extra > SIZE_MAX - self->first_unused_idx) {
1,545,534✔
310
    TEN_ASSERT(0, "Size overflow detected.");
×
311
    return;
×
312
  }
×
313

314
  size_t required_size = self->first_unused_idx + extra;
1,545,534✔
315
  if (required_size > (size_t)MAX_BUFFER_SIZE) {
1,545,534✔
316
#if !defined(OS_WINDOWS)
×
317
    (void)dprintf(
×
318
        STDERR_FILENO,
×
319
        "Buffer size exceeds the maximum limit when ten_string_reserve, "
×
320
        "required_size: %zu\n",
×
321
        required_size);
×
322
#endif
×
323
  }
×
324

325
  if (self->buf_size < required_size) {
1,545,534✔
326
    size_t new_size = self->buf_size * BUFFER_ENLARGE_RATIO;
1,545,405✔
327
    if (new_size < required_size) {
1,545,405✔
328
      new_size = required_size;
8,334✔
329
    }
8,334✔
330

331
    char *tmp = NULL;
1,545,405✔
332
    if (self->buf == self->pre_buf) {
1,545,405✔
333
      tmp = (char *)TEN_MALLOC(new_size);
1,545,267✔
334
      TEN_ASSERT(tmp, "Failed to allocate memory.");
1,545,267✔
335

336
      memcpy(tmp, self->buf, self->first_unused_idx);
1,545,267✔
337
    } else {
1,545,267✔
338
      tmp = (char *)TEN_REALLOC(self->buf, new_size);
138✔
339
      TEN_ASSERT(tmp, "Failed to allocate memory.");
138✔
340
    }
138✔
341

342
    self->buf = tmp;
1,545,405✔
343
    self->buf_size = new_size;
1,545,405✔
344
  }
1,545,405✔
345
}
1,545,534✔
346

347
bool ten_string_is_empty(const ten_string_t *self) {
195,947✔
348
  TEN_ASSERT(self, "Invalid argument.");
195,947✔
349
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
195,947✔
350
  return ten_c_string_is_empty(self->buf);
195,947✔
351
}
195,947✔
352

353
bool ten_string_starts_with(const ten_string_t *self, const char *start) {
2,074✔
354
  TEN_ASSERT(self && ten_string_check_integrity(self) && start,
2,074✔
355
             "Invalid argument.");
2,074✔
356

357
  int index = ten_c_string_index_of(ten_string_get_raw_str(self), start);
2,074✔
358
  return index == 0;
2,074✔
359
}
2,074✔
360

361
void ten_string_erase_back(ten_string_t *self, size_t count) {
75✔
362
  TEN_ASSERT(self, "Invalid argument.");
75✔
363
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
75✔
364

365
  size_t len = strlen(self->buf);
75✔
366
  if (count > len) {
75✔
367
    count = len;
×
368
  }
×
369
  self->buf[len - count] = '\0';
75✔
370
}
75✔
371

372
// Compare 'a' & 'b' up to 'num' characters.
373
static int strcmp_case_insensitive(const char *a, const char *b, size_t num) {
1✔
374
  for (size_t i = 0; i < num; a++, b++, i++) {
15✔
375
    int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
14✔
376
    if (d != 0 || !*a) {
14✔
377
      return d;
×
378
    }
×
379
  }
14✔
380
  return 0;
1✔
381
}
1✔
382

383
bool ten_c_string_is_equal(const char *a, const char *b) {
2,711,986✔
384
  TEN_ASSERT(a && b, "Invalid argument.");
2,711,986✔
385
  return (0 == strcmp(a, b)) ? true : false;
2,711,986✔
386
}
2,711,986✔
387

388
bool ten_c_string_is_equal_with_size(const char *a, const char *b, size_t num) {
1✔
389
  TEN_ASSERT(a && b, "Invalid argument.");
1✔
390
  return (0 == strncmp(a, b, num)) ? true : false;
1✔
391
}
1✔
392

393
bool ten_c_string_is_equal_case_insensitive(const char *a, const char *b) {
1✔
394
  TEN_ASSERT(a && b, "Invalid argument.");
1✔
395
  return (0 == strcmp_case_insensitive(a, b, strlen(a))) ? true : false;
1✔
396
}
1✔
397

398
bool ten_c_string_is_equal_with_size_case_insensitive(const char *a,
399
                                                      const char *b,
400
                                                      size_t num) {
×
401
  TEN_ASSERT(a && b, "Invalid argument.");
×
402
  return (0 == strcmp_case_insensitive(a, b, num)) ? true : false;
×
403
}
×
404

405
bool ten_c_string_contains(const char *a, const char *b) {
1✔
406
  TEN_ASSERT(a && b, "Invalid argument.");
1✔
407
  return strstr(a, b) != NULL;
1✔
408
}
1✔
409

410
bool ten_string_is_equal(const ten_string_t *a, const ten_string_t *b) {
97,282✔
411
  TEN_ASSERT(
97,282✔
412
      a && ten_string_check_integrity(a) && b && ten_string_check_integrity(b),
97,282✔
413
      "Invalid argument.");
97,282✔
414
  return ten_c_string_is_equal(ten_string_get_raw_str(a),
97,282✔
415
                               ten_string_get_raw_str(b));
97,282✔
416
}
97,282✔
417

418
bool ten_string_is_equal_c_str(ten_string_t *a, const char *b) {
2,295,498✔
419
  TEN_ASSERT(a && ten_string_check_integrity(a) && b, "Invalid argument.");
2,295,498✔
420
  return ten_c_string_is_equal(ten_string_get_raw_str(a), b);
2,295,498✔
421
}
2,295,498✔
422

423
bool ten_string_is_equal_c_str_case_insensitive(ten_string_t *a,
424
                                                const char *b) {
1✔
425
  TEN_ASSERT(a && ten_string_check_integrity(a) && b, "Invalid argument.");
1✔
426
  return ten_c_string_is_equal_case_insensitive(ten_string_get_raw_str(a), b);
1✔
427
}
1✔
428

429
bool ten_string_contains(ten_string_t *self, const char *b) {
×
430
  TEN_ASSERT(self && ten_string_check_integrity(self) && b,
×
431
             "Invalid argument.");
×
432
  return ten_c_string_contains(ten_string_get_raw_str(self), b);
×
433
}
×
434

435
void ten_string_to_lower(ten_string_t *self) {
×
436
  TEN_ASSERT(self, "Invalid argument.");
×
437
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
×
438
  for (size_t i = 0; i < ten_string_len(self); ++i) {
×
439
    self->buf[i] = tolower(self->buf[i]);
×
440
  }
×
441
}
×
442

443
void ten_string_to_upper(ten_string_t *self) {
×
444
  TEN_ASSERT(self, "Invalid argument.");
×
445
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
×
446
  for (size_t i = 0; i < ten_string_len(self); ++i) {
×
447
    self->buf[i] = toupper(self->buf[i]);
×
448
  }
×
449
}
×
450

451
bool ten_c_string_is_empty(const char *str) {
288,158✔
452
  TEN_ASSERT(str, "Invalid argument.");
288,158✔
453
  return str[0] == 0;
288,158✔
454
}
288,158✔
455

456
bool ten_c_string_starts_with(const char *str, const char *prefix) {
618✔
457
  TEN_ASSERT(str && prefix, "Invalid argument.");
618✔
458
  return strncmp(prefix, str, strlen(prefix)) == 0;
618✔
459
}
618✔
460

461
bool ten_c_string_ends_with(const char *str, const char *postfix) {
×
462
  TEN_ASSERT(str && postfix, "Invalid argument.");
×
463
  return strncmp(postfix, str + strlen(str) - strlen(postfix),
×
464
                 strlen(postfix)) == 0;
×
465
}
×
466

467
bool ten_c_string_is_equal_or_smaller(const char *a, const char *b) {
2✔
468
  TEN_ASSERT(a && b, "Invalid argument.");
2✔
469

470
  if (strlen(a) < strlen(b)) {
2✔
471
    return true;
×
472
  } else if (strlen(a) > strlen(b)) {
2✔
473
    return false;
×
474
  } else {
2✔
475
    for (size_t i = 0; i < strlen(a); ++i) {
48✔
476
      if (a[i] < b[i]) {
48✔
477
        return true;
1✔
478
      } else if (a[i] > b[i]) {
47✔
479
        return false;
1✔
480
      }
1✔
481
    }
48✔
482
    return true;
×
483
  }
2✔
484
}
2✔
485

486
int ten_c_string_index_of(const char *src, const char *search) {
2,074✔
487
  TEN_ASSERT(src && search, "Invalid argument.");
2,074✔
488

489
  const char *match = strstr(src, search);
2,074✔
490
  if (match) {
2,074✔
491
    return match - src;
174✔
492
  } else {
1,900✔
493
    return -1;
1,900✔
494
  }
1,900✔
495
}
2,074✔
496

497
void ten_c_string_split(const char *src, const char *delimiter,
498
                        ten_list_t *result) {
187,611✔
499
  TEN_ASSERT(src && delimiter && result, "Invalid argument.");
187,611✔
500

501
  // Because strtok would modify the string content, we need to clone the
502
  // string first.
503
  char *tmp = (char *)TEN_MALLOC(strlen(src) + 1);
187,611✔
504
  TEN_ASSERT(tmp, "Failed to allocate memory.");
187,611✔
505

506
  memcpy(tmp, src, strlen(src) + 1);
187,611✔
507

508
  char *save = NULL;
187,611✔
509
  char *token = strtok_r(tmp, delimiter, &save);
187,611✔
510
  while (token) {
377,170✔
511
    ten_list_push_str_back(result, token);
189,559✔
512
    token = strtok_r(NULL, delimiter, &save);
189,559✔
513
  }
189,559✔
514

515
  TEN_FREE(tmp);
187,611✔
516
}
187,611✔
517

518
void ten_string_split(ten_string_t *self, const char *delimiter,
519
                      ten_list_t *result) {
92,960✔
520
  TEN_ASSERT(self, "Invalid argument.");
92,960✔
521
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
92,960✔
522
  TEN_ASSERT(delimiter, "Invalid argument.");
92,960✔
523
  TEN_ASSERT(result && ten_list_check_integrity(result), "Invalid argument.");
92,960✔
524

525
  return ten_c_string_split(ten_string_get_raw_str(self), delimiter, result);
92,960✔
526
}
92,960✔
527

528
static const char *uri_encode_tbl[0x100] = {
529
    "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", "%0A",
530
    "%0B", "%0C", "%0D", "%0E", "%0F", "%10", "%11", "%12", "%13", "%14", "%15",
531
    "%16", "%17", "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", "%20",
532
    "%21", "%22", "%23", "%24", "%25", "%26", "%27", "%28", "%29", "%2A", "%2B",
533
    "%2C", NULL,  NULL,  "%2F", NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
534
    NULL,  NULL,  NULL,  "%3A", "%3B", "%3C", "%3D", "%3E", "%3F", "%40", NULL,
535
    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
536
    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
537
    NULL,  NULL,  NULL,  "%5B", "%5C", "%5D", "%5E", NULL,  "%60", NULL,  NULL,
538
    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
539
    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
540
    NULL,  NULL,  "%7B", "%7C", "%7D", NULL,  "%7F", "%80", "%81", "%82", "%83",
541
    "%84", "%85", "%86", "%87", "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E",
542
    "%8F", "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99",
543
    "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", "%A0", "%A1", "%A2", "%A3", "%A4",
544
    "%A5", "%A6", "%A7", "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF",
545
    "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", "%B8", "%B9", "%BA",
546
    "%BB", "%BC", "%BD", "%BE", "%BF", "%C0", "%C1", "%C2", "%C3", "%C4", "%C5",
547
    "%C6", "%C7", "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", "%D0",
548
    "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB",
549
    "%DC", "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6",
550
    "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", "%F0", "%F1",
551
    "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8", "%F9", "%FA", "%FB", "%FC",
552
    "%FD", "%FE", "%FF",
553
};
554

555
#define __ 0xFF
556
static const unsigned char hex_val_table[0x100] = {
557
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* 00-0F */
558
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* 10-1F */
559
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* 20-2F */
560
    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  __, __, __, __, __, __, /* 30-3F */
561
    __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, /* 40-4F */
562
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* 50-5F */
563
    __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, /* 60-6F */
564
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* 70-7F */
565
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* 80-8F */
566
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* 90-9F */
567
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* A0-AF */
568
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* B0-BF */
569
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* C0-CF */
570
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* D0-DF */
571
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* E0-EF */
572
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, /* F0-FF */
573
};
574
#undef __
575

576
void ten_c_string_uri_encode(const char *src, const size_t len,
577
                             ten_string_t *result) {
×
578
  TEN_ASSERT(src && result && ten_string_check_integrity(result),
×
579
             "Invalid argument.");
×
580

581
  size_t i = 0;
×
582
  while (i < len) {
×
583
    const char octet = src[i++];
×
584
    const char *code = uri_encode_tbl[(int8_t)octet];
×
585
    if (code) {
×
586
      ten_string_append_formatted(result, "%s", code);
×
587
    } else {
×
588
      ten_string_append_formatted(result, "%c", octet);
×
589
    }
×
590
  }
×
591
}
×
592

593
void ten_c_string_uri_decode(const char *src, const size_t len,
594
                             ten_string_t *result) {
×
595
  TEN_ASSERT(result && ten_string_check_integrity(result), "Invalid argument.");
×
596

597
  size_t i = 0;
×
598
  while (i < len) {
×
599
    bool copy_char = true;
×
600
    if (src[i] == '%' && i + 2 < len) {
×
601
      const unsigned char v1 = hex_val_table[(unsigned char)src[i + 1]];
×
602
      const unsigned char v2 = hex_val_table[(unsigned char)src[i + 2]];
×
603

604
      // skip invalid hex sequences
605
      if ((v1 | v2) != 0xFF) {
×
606
        ten_string_append_formatted(result, "%c", (v1 << 4) | v2);
×
607
        i += 3;
×
608
        copy_char = false;
×
609
      }
×
610
    }
×
611
    if (copy_char) {
×
612
      ten_string_append_formatted(result, "%c", src[i]);
×
613
      i++;
×
614
    }
×
615
  }
×
616
}
×
617

618
void ten_string_hex_from_buf(ten_string_t *self, ten_buf_t buf) {
×
619
  TEN_ASSERT(self, "Invalid argument.");
×
620
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
×
621

622
  for (size_t i = 0; i < buf.content_size; i++) {
×
623
    ten_string_append_formatted(self, "%02x", (buf.data)[i]);
×
624
  }
×
625
}
×
626

627
void ten_string_trim_trailing_slash(ten_string_t *self) {
×
628
  TEN_ASSERT(self, "Invalid argument.");
×
629
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
×
630

631
  for (size_t i = ten_string_len(self) - 1;
×
632
       i >= 0 && ((self->buf[i] == '/') || (self->buf[i] == '\\')); i--) {
×
633
    self->buf[i] = '\0';
×
634
    self->first_unused_idx--;
×
635
  }
×
636
}
×
637

638
void ten_string_trim_trailing_whitespace(ten_string_t *self) {
×
639
  TEN_ASSERT(self, "Invalid argument.");
×
640
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
×
641

642
  for (size_t i = ten_string_len(self) - 1; i >= 0 && isspace(self->buf[i]);
×
643
       i--) {
×
644
    self->buf[i] = '\0';
×
645
    self->first_unused_idx--;
×
646
  }
×
647
}
×
648

649
void ten_string_trim_leading_whitespace(ten_string_t *self) {
×
650
  TEN_ASSERT(self, "Invalid argument.");
×
651
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
×
652

653
  size_t whitespace_cnt = 0;
×
654
  for (size_t i = 0; i < ten_string_len(self); i++) {
×
655
    if (isspace(self->buf[i])) {
×
656
      whitespace_cnt++;
×
657
    } else {
×
658
      break;
×
659
    }
×
660
  }
×
661

662
  if (whitespace_cnt) {
×
663
    memmove(self->buf, self->buf + whitespace_cnt,
×
664
            self->first_unused_idx - whitespace_cnt);
×
665
    self->first_unused_idx -= whitespace_cnt;
×
666
    self->buf[self->first_unused_idx] = '\0';
×
667
  }
×
668
}
×
669

670
char *ten_c_string_trim_trailing_whitespace(char *self) {
×
671
  TEN_ASSERT(self, "Invalid argument.");
×
672

673
  for (size_t i = strlen(self) - 1; i >= 0 && isspace(self[i]); i--) {
×
674
    self[i] = '\0';
×
675
  }
×
676
  return self;
×
677
}
×
678

679
void ten_c_string_escaped(const char *src, ten_string_t *result) {
×
680
  TEN_ASSERT(src && result && ten_string_check_integrity(result),
×
681
             "Invalid argument.");
×
682

683
  for (size_t i = 0; i < strlen(src); i++) {
×
684
    char byte = src[i];
×
685
    switch (byte) {
×
686
    case 0x08: {  // backspace
×
687
      ten_string_append_formatted(result, "%c", '\\');
×
688
      ten_string_append_formatted(result, "%c", 'b');
×
689
      break;
×
690
    }
×
691
    case 0x09: {  // horizontal tab
×
692
      ten_string_append_formatted(result, "%c", '\\');
×
693
      ten_string_append_formatted(result, "%c", 't');
×
694
      break;
×
695
    }
×
696
    case 0x0A: {  // newline
×
697
      ten_string_append_formatted(result, "%c", '\\');
×
698
      ten_string_append_formatted(result, "%c", 'n');
×
699
      break;
×
700
    }
×
701
    case 0x0C: {  // formfeed
×
702
      ten_string_append_formatted(result, "%c", '\\');
×
703
      ten_string_append_formatted(result, "%c", 'f');
×
704
      break;
×
705
    }
×
706
    case 0x0D: {  // carriage return
×
707
      ten_string_append_formatted(result, "%c", '\\');
×
708
      ten_string_append_formatted(result, "%c", 'r');
×
709
      break;
×
710
    }
×
711
    case 0x22: {  // quotation mark
×
712
      ten_string_append_formatted(result, "%c", '\\');
×
713
      ten_string_append_formatted(result, "%c", '\"');
×
714
      break;
×
715
    }
×
716
    case 0x5C: {  // reverse solidus
×
717
      ten_string_append_formatted(result, "%c", '\\');
×
718
      ten_string_append_formatted(result, "%c", '\\');
×
719
      break;
×
720
    }
×
721
    default: {
×
722
      // escape control characters (0x00..0x1F) or, if
723
      // ensure_ascii parameter is used, non-ASCII characters
724
      if (byte <= 0x1F) {
×
725
        ten_string_append_formatted(result, "\\u%04x", byte);
×
726
      } else {
×
727
        ten_string_append_formatted(result, "%c", byte);
×
728
      }
×
729
      break;
×
730
    }
×
731
    }
×
732
  }
×
733
}
×
734

735
bool ten_raw_string_is_uuid4(const char *self) {
1,605✔
736
  TEN_ASSERT(self, "Invalid argument.");
1,605✔
737

738
  if (strlen(self) != 36) {
1,605✔
739
    return false;
66✔
740
  }
66✔
741

742
  for (size_t i = 0; i < strlen(self); ++i) {
56,943✔
743
    if (i == 8 || i == 13 || i == 18 || i == 23) {
55,404✔
744
      if (self[i] != '-') {
6,156✔
745
        return false;
×
746
      }
×
747
    } else {
49,248✔
748
      if (self[i] < '0' || self[i] > 'f') {
49,248✔
749
        return false;
×
750
      }
×
751
    }
49,248✔
752
  }
55,404✔
753

754
  return true;
1,539✔
755
}
1,539✔
756

757
void ten_string_slice(ten_string_t *self, ten_string_t *other, char sep) {
×
758
  TEN_ASSERT(self && ten_string_check_integrity(self) && other &&
×
759
                 ten_string_check_integrity(other),
×
760
             "Invalid argument.");
×
761

762
  // Find separator in buf.
763
  char *pr = strchr(ten_string_get_raw_str(self), sep);
×
764
  if (pr == NULL) {
×
765
    return;
×
766
  }
×
767

768
  // Length from separator to end of buf.
769
  size_t plen = strlen(pr);
×
770

771
  // Copy from self into 'other' string.
772
  int int_plen = 0;
×
773
  bool rc = safe_cast_size_t_to_int(plen, &int_plen);
×
774
  TEN_ASSERT(rc, "Length overflow detected.");
×
775

776
  ten_string_init_formatted(other, "%.*s", int_plen, pr + 1);
×
777

778
  *pr = '\0';
×
779
  self->first_unused_idx = (pr - self->buf);
×
780
}
×
781

782
extern inline const char *ten_string_get_raw_str(const ten_string_t *self);
783

784
extern inline size_t ten_string_len(const ten_string_t *self);
785

786
extern inline bool ten_string_check_integrity(const ten_string_t *self);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc