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

TEN-framework / ten-framework / 20717203581

05 Jan 2026 01:40PM UTC coverage: 57.744% (-0.002%) from 57.746%
20717203581

Pull #1944

github

web-flow
Merge 935c3ff1c into 62a343cb1
Pull Request #1944: chore: debug cpp integration tests when compiled with mingw

54425 of 94252 relevant lines covered (57.74%)

662891.77 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,679✔
31
  ten_string_t *self = (ten_string_t *)TEN_MALLOC(sizeof(ten_string_t));
36,679✔
32
  TEN_ASSERT(self, "Failed to allocate memory.");
36,679✔
33

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

36
  return self;
36,679✔
37
}
36,679✔
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,419,343✔
62
  TEN_ASSERT(self, "Invalid argument.");
2,419,343✔
63
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
2,419,343✔
64

65
  size_t retry_count = 0;
2,419,343✔
66

67
  va_list cp;
2,419,343✔
68
  va_copy(cp, ap);
2,419,343✔
69

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

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

77
    va_end(temp);
3,979,864✔
78

79
    if ((n > -1) && ((size_t)n < (self->buf_size - self->first_unused_idx))) {
3,979,864✔
80
      self->first_unused_idx += n;
2,419,342✔
81
      va_end(cp);
2,419,342✔
82
      return;
2,419,342✔
83
    }
2,419,342✔
84

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

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

95
      TEN_ASSERT(0, "Should not happen");
×
96
      return;
×
97
    }
×
98
  }
1,560,522✔
99
}
2,419,343✔
100

101
ten_string_t *ten_string_create_formatted(const char *fmt, ...) {
36,646✔
102
  ten_string_t *self = ten_string_create();
36,646✔
103
  va_list ap;
36,646✔
104
  va_start(ap, fmt);
36,646✔
105
  ten_string_append_from_va_list(self, fmt, ap);
36,646✔
106
  va_end(ap);
36,646✔
107
  return self;
36,646✔
108
}
36,646✔
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,720✔
117
  TEN_ASSERT(other && ten_string_check_integrity(other), "Invalid argument.");
11,720✔
118

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

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

129
void ten_string_init(ten_string_t *self) {
18,635✔
130
  TEN_ASSERT(self, "Invalid argument.");
18,635✔
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,635✔
135

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

219
  ten_string_clear(self);
1,034,179✔
220

221
  va_list ap;
1,034,179✔
222
  va_start(ap, fmt);
1,034,179✔
223
  ten_string_append_from_va_list(self, fmt, ap);
1,034,179✔
224
  va_end(ap);
1,034,179✔
225
}
1,034,179✔
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, ...) {
492,219✔
268
  TEN_ASSERT(self, "Invalid argument.");
492,219✔
269
  TEN_ASSERT(ten_string_check_integrity(self), "Invalid argument.");
492,219✔
270
  TEN_ASSERT(fmt, "Invalid argument.");
492,219✔
271

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

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

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

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

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

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

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

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

314
  size_t required_size = self->first_unused_idx + extra;
1,561,074✔
315
  if (required_size > (size_t)MAX_BUFFER_SIZE) {
1,561,074✔
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,561,074✔
326
    size_t new_size = self->buf_size * BUFFER_ENLARGE_RATIO;
1,560,944✔
327
    if (new_size < required_size) {
1,560,944✔
328
      new_size = required_size;
7,901✔
329
    }
7,901✔
330

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

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

342
    self->buf = tmp;
1,560,944✔
343
    self->buf_size = new_size;
1,560,944✔
344
  }
1,560,944✔
345
}
1,561,074✔
346

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

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

357
  int index = ten_c_string_index_of(ten_string_get_raw_str(self), start);
2,075✔
358
  return index == 0;
2,075✔
359
}
2,075✔
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,719,692✔
384
  TEN_ASSERT(a && b, "Invalid argument.");
2,719,692✔
385
  return (0 == strcmp(a, b)) ? true : false;
2,719,692✔
386
}
2,719,692✔
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,499✔
411
  TEN_ASSERT(
97,499✔
412
      a && ten_string_check_integrity(a) && b && ten_string_check_integrity(b),
97,499✔
413
      "Invalid argument.");
97,499✔
414
  return ten_c_string_is_equal(ten_string_get_raw_str(a),
97,499✔
415
                               ten_string_get_raw_str(b));
97,499✔
416
}
97,499✔
417

418
bool ten_string_is_equal_c_str(ten_string_t *a, const char *b) {
2,303,162✔
419
  TEN_ASSERT(a && ten_string_check_integrity(a) && b, "Invalid argument.");
2,303,162✔
420
  return ten_c_string_is_equal(ten_string_get_raw_str(a), b);
2,303,162✔
421
}
2,303,162✔
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) {
307,073✔
452
  TEN_ASSERT(str, "Invalid argument.");
307,073✔
453
  return str[0] == 0;
307,073✔
454
}
307,073✔
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,075✔
487
  TEN_ASSERT(src && search, "Invalid argument.");
2,075✔
488

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

497
void ten_c_string_split(const char *src, const char *delimiter,
498
                        ten_list_t *result) {
187,844✔
499
  TEN_ASSERT(src && delimiter && result, "Invalid argument.");
187,844✔
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,844✔
504
  TEN_ASSERT(tmp, "Failed to allocate memory.");
187,844✔
505

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

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

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

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

525
  return ten_c_string_split(ten_string_get_raw_str(self), delimiter, result);
93,076✔
526
}
93,076✔
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
/*
783
Cross-DLL usage: These inline functions are used in ten_runtime
784
(ten_runtime.dll -> ten_utils.dll)
785

786
TEN_UTILS_API is used for DLL export on Windows(MinGW). Without it,
787
error "undefined reference" will be raised.
788

789
According to GNU11 standard for inline functions, the "extern" keyword
790
should be used in .c file instead of .h file to prevent multiple definition.
791
So TEN_UTILS_API, which contains "extern" keyword, should be used here in .c
792
file.
793

794
Why only Windows(MinGW) needs this (not Linux/macOS/MSVC):
795
1. Linux/macOS: Global symbols declared with "extern" keyword are exported by
796
default.
797
2. Windows(MSVC): Each DLL generates and uses its own COMDAT copy of inline
798
functions, eliminating the need for cross-DLL imports.
799

800
Another solution:
801
MinGW uses GNU11 standard in this project, but we can use
802
__attribute__((gnu_inline)) in both .c and .h file to force the "inline"
803
keyword to work in GNU89 standard, which is exactly the opposite way.
804
("extern" keyword is used in .h file to prevent multiple definition)
805
And then TEN_UTILS_API can be used in .h file like the other functions.
806
*/
807
#if defined(__MINGW32__) || defined(__MINGW64__)
808
TEN_UTILS_API inline const char *ten_string_get_raw_str(
809
    const ten_string_t *self);
810

811
TEN_UTILS_API inline size_t ten_string_len(const ten_string_t *self);
812

813
TEN_UTILS_API inline bool ten_string_check_integrity(const ten_string_t *self);
814
#else
815
extern inline const char *ten_string_get_raw_str(const ten_string_t *self);
816

817
extern inline size_t ten_string_len(const ten_string_t *self);
818

819
extern inline bool ten_string_check_integrity(const ten_string_t *self);
820
#endif
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