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

tarantool / luajit / 5784751762

07 Aug 2023 11:54AM UTC coverage: 84.282% (-3.1%) from 87.373%
5784751762

push

github

ligurio
setup-gcovr [TO SQUASH]

5157 of 6002 branches covered (85.92%)

Branch coverage included in aggregate %.

19632 of 23410 relevant lines covered (83.86%)

229380.51 hits per line

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

64.92
/src/lib_string.c
1
/*
2
** String library.
3
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
4
**
5
** Major portions taken verbatim or adapted from the Lua interpreter.
6
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
7
*/
8

9
#define lib_string_c
10
#define LUA_LIB
11

12
#include "lua.h"
13
#include "lauxlib.h"
14
#include "lualib.h"
15

16
#include "lj_obj.h"
17
#include "lj_gc.h"
18
#include "lj_err.h"
19
#include "lj_buf.h"
20
#include "lj_str.h"
21
#include "lj_tab.h"
22
#include "lj_meta.h"
23
#include "lj_state.h"
24
#include "lj_ff.h"
25
#include "lj_bcdump.h"
26
#include "lj_char.h"
27
#include "lj_strfmt.h"
28
#include "lj_lib.h"
29

30
/* ------------------------------------------------------------------------ */
31

32
#define LJLIB_MODULE_string
33

34
LJLIB_LUA(string_len) /*
35
  function(s)
36
    CHECK_str(s)
37
    return #s
38
  end
39
*/
40

41
LJLIB_ASM(string_byte)                LJLIB_REC(string_range 0)
96✔
42
{
43
  GCstr *s = lj_lib_checkstr(L, 1);
96✔
44
  int32_t len = (int32_t)s->len;
96✔
45
  int32_t start = lj_lib_optint(L, 2, 1);
96✔
46
  int32_t stop = lj_lib_optint(L, 3, start);
96✔
47
  int32_t n, i;
96✔
48
  const unsigned char *p;
96✔
49
  if (stop < 0) stop += len+1;
96✔
50
  if (start < 0) start += len+1;
96✔
51
  if (start <= 0) start = 1;
96✔
52
  if (stop > len) stop = len;
96✔
53
  if (start > stop) return FFH_RES(0);  /* Empty interval: return no results. */
96✔
54
  start--;
96✔
55
  n = stop - start;
96✔
56
  if ((uint32_t)n > LUAI_MAXCSTACK)
96✔
57
    lj_err_caller(L, LJ_ERR_STRSLC);
×
58
  lj_state_checkstack(L, (MSize)n);
96✔
59
  p = (const unsigned char *)strdata(s) + start;
96✔
60
  for (i = 0; i < n; i++)
192✔
61
    setintV(L->base + i-1-LJ_FR2, p[i]);
96✔
62
  return FFH_RES(n);
96✔
63
}
64

65
LJLIB_ASM(string_char)                LJLIB_REC(.)
×
66
{
67
  int i, nargs = (int)(L->top - L->base);
×
68
  char *buf = lj_buf_tmp(L, (MSize)nargs);
×
69
  for (i = 1; i <= nargs; i++) {
×
70
    int32_t k = lj_lib_checkint(L, i);
×
71
    if (!checku8(k))
×
72
      lj_err_arg(L, i, LJ_ERR_BADVAL);
×
73
    buf[i-1] = (char)k;
×
74
  }
75
  setstrV(L, L->base-1-LJ_FR2, lj_str_new(L, buf, (size_t)nargs));
×
76
  return FFH_RES(1);
×
77
}
78

79
LJLIB_ASM(string_sub)                LJLIB_REC(string_range 1)
×
80
{
81
  lj_lib_checkstr(L, 1);
×
82
  lj_lib_checkint(L, 2);
×
83
  setintV(L->base+2, lj_lib_optint(L, 3, -1));
×
84
  return FFH_RETRY;
×
85
}
86

87
LJLIB_CF(string_rep)                LJLIB_REC(.)
×
88
{
89
  GCstr *s = lj_lib_checkstr(L, 1);
×
90
  int32_t rep = lj_lib_checkint(L, 2);
×
91
  GCstr *sep = lj_lib_optstr(L, 3);
×
92
  SBuf *sb = lj_buf_tmp_(L);
×
93
  if (sep && rep > 1) {
×
94
    GCstr *s2 = lj_buf_cat2str(L, sep, s);
×
95
    lj_buf_reset(sb);
×
96
    lj_buf_putstr(sb, s);
×
97
    s = s2;
×
98
    rep--;
×
99
  }
100
  sb = lj_buf_putstr_rep(sb, s, rep);
×
101
  setstrV(L, L->top-1, lj_buf_str(L, sb));
×
102
  lj_gc_check(L);
×
103
  return 1;
×
104
}
105

106
LJLIB_ASM(string_reverse)  LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse)
×
107
{
108
  lj_lib_checkstr(L, 1);
×
109
  return FFH_RETRY;
×
110
}
111
LJLIB_ASM_(string_lower)  LJLIB_REC(string_op IRCALL_lj_buf_putstr_lower)
112
LJLIB_ASM_(string_upper)  LJLIB_REC(string_op IRCALL_lj_buf_putstr_upper)
113

114
/* ------------------------------------------------------------------------ */
115

116
static int writer_buf(lua_State *L, const void *p, size_t size, void *sb)
18✔
117
{
118
  lj_buf_putmem((SBuf *)sb, p, (MSize)size);
18✔
119
  UNUSED(L);
18✔
120
  return 0;
18✔
121
}
122

123
LJLIB_CF(string_dump)
6✔
124
{
125
  GCfunc *fn = lj_lib_checkfunc(L, 1);
6✔
126
  int strip = L->base+1 < L->top && tvistruecond(L->base+1);
6✔
127
  SBuf *sb = lj_buf_tmp_(L);  /* Assumes lj_bcwrite() doesn't use tmpbuf. */
6✔
128
  L->top = L->base+1;
6✔
129
  if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, sb, strip))
6✔
130
    lj_err_caller(L, LJ_ERR_STRDUMP);
×
131
  setstrV(L, L->top-1, lj_buf_str(L, sb));
6✔
132
  lj_gc_check(L);
6✔
133
  return 1;
6✔
134
}
135

136
/* ------------------------------------------------------------------------ */
137

138
/* macro to `unsign' a character */
139
#define uchar(c)        ((unsigned char)(c))
140

141
#define CAP_UNFINISHED        (-1)
142
#define CAP_POSITION        (-2)
143

144
typedef struct MatchState {
145
  const char *src_init;  /* init of source string */
146
  const char *src_end;  /* end (`\0') of source string */
147
  lua_State *L;
148
  int level;  /* total number of captures (finished or unfinished) */
149
  int depth;
150
  struct {
151
    const char *init;
152
    ptrdiff_t len;
153
  } capture[LUA_MAXCAPTURES];
154
} MatchState;
155

156
#define L_ESC                '%'
157

158
static int check_capture(MatchState *ms, int l)
×
159
{
160
  l -= '1';
×
161
  if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
×
162
    lj_err_caller(ms->L, LJ_ERR_STRCAPI);
×
163
  return l;
×
164
}
165

166
static int capture_to_close(MatchState *ms)
16✔
167
{
168
  int level = ms->level;
16✔
169
  for (level--; level>=0; level--)
16✔
170
    if (ms->capture[level].len == CAP_UNFINISHED) return level;
16✔
171
  lj_err_caller(ms->L, LJ_ERR_STRPATC);
×
172
  return 0;  /* unreachable */
173
}
174

175
static const char *classend(MatchState *ms, const char *p)
176
{
177
  switch (*p++) {
178
  case L_ESC:
179
    if (*p == '\0')
180
      lj_err_caller(ms->L, LJ_ERR_STRPATE);
181
    return p+1;
182
  case '[':
183
    if (*p == '^') p++;
184
    do {  /* look for a `]' */
185
      if (*p == '\0')
186
        lj_err_caller(ms->L, LJ_ERR_STRPATM);
187
      if (*(p++) == L_ESC && *p != '\0')
188
        p++;  /* skip escapes (e.g. `%]') */
189
    } while (*p != ']');
190
    return p+1;
191
  default:
192
    return p;
193
  }
194
}
195

196
static const unsigned char match_class_map[32] = {
197
  0,LJ_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0,
198
  LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0,
199
  LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0
200
};
201

202
static int match_class(int c, int cl)
694✔
203
{
204
  if ((cl & 0xc0) == 0x40) {
694✔
205
    int t = match_class_map[(cl&0x1f)];
564✔
206
    if (t) {
564✔
207
      t = lj_char_isa(c, t);
564✔
208
      return (cl & 0x20) ? t : !t;
564✔
209
    }
210
    if (cl == 'z') return c == 0;
×
211
    if (cl == 'Z') return c != 0;
×
212
  }
213
  return (cl == c);
130✔
214
}
215

216
static int matchbracketclass(int c, const char *p, const char *ec)
11,397✔
217
{
218
  int sig = 1;
11,397✔
219
  if (*(p+1) == '^') {
11,397✔
220
    sig = 0;
11,356✔
221
    p++;  /* skip the `^' */
11,356✔
222
  }
223
  while (++p < ec) {
21,734✔
224
    if (*p == L_ESC) {
11,451✔
225
      p++;
56✔
226
      if (match_class(c, uchar(*p)))
56✔
227
        return sig;
228
    }
229
    else if ((*(p+1) == '-') && (p+2 < ec)) {
11,395✔
230
      p+=2;
×
231
      if (uchar(*(p-2)) <= c && c <= uchar(*p))
×
232
        return sig;
233
    }
234
    else if (uchar(*p) == c) return sig;
11,395✔
235
  }
236
  return !sig;
10,283✔
237
}
238

239
static int singlematch(int c, const char *p, const char *ep)
15,449✔
240
{
241
  switch (*p) {
15,449✔
242
  case '.': return 1;  /* matches any char */
243
  case L_ESC: return match_class(c, uchar(*(p+1)));
638✔
244
  case '[': return matchbracketclass(c, p, ep-1);
11,397✔
245
  default:  return (uchar(*p) == c);
3,311✔
246
  }
247
}
248

249
static const char *match(MatchState *ms, const char *s, const char *p);
250

251
static const char *matchbalance(MatchState *ms, const char *s, const char *p)
252
{
253
  if (*p == 0 || *(p+1) == 0)
254
    lj_err_caller(ms->L, LJ_ERR_STRPATU);
255
  if (*s != *p) {
256
    return NULL;
257
  } else {
258
    int b = *p;
259
    int e = *(p+1);
260
    int cont = 1;
261
    while (++s < ms->src_end) {
262
      if (*s == e) {
263
        if (--cont == 0) return s+1;
264
      } else if (*s == b) {
265
        cont++;
266
      }
267
    }
268
  }
269
  return NULL;  /* string ends out of balance */
270
}
271

272
static const char *max_expand(MatchState *ms, const char *s,
1,101✔
273
                              const char *p, const char *ep)
274
{
275
  ptrdiff_t i = 0;  /* counts maximum expand for item */
1,101✔
276
  while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
10,724✔
277
    i++;
9,623✔
278
  /* keeps trying to match with the maximum repetitions */
279
  while (i>=0) {
6,405✔
280
    const char *res = match(ms, (s+i), ep+1);
5,525✔
281
    if (res) return res;
5,525✔
282
    i--;  /* else didn't match; reduce 1 repetition to try again */
5,304✔
283
  }
284
  return NULL;
285
}
286

287
static const char *min_expand(MatchState *ms, const char *s,
7✔
288
                              const char *p, const char *ep)
289
{
290
  for (;;) {
137✔
291
    const char *res = match(ms, s, ep+1);
72✔
292
    if (res != NULL)
72✔
293
      return res;
7✔
294
    else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
65✔
295
      s++;  /* try with one more repetition */
65✔
296
    else
297
      return NULL;
298
  }
299
}
300

301
static const char *start_capture(MatchState *ms, const char *s,
8✔
302
                                 const char *p, int what)
303
{
304
  const char *res;
8✔
305
  int level = ms->level;
8✔
306
  if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
8✔
307
  ms->capture[level].init = s;
8✔
308
  ms->capture[level].len = what;
8✔
309
  ms->level = level+1;
8✔
310
  if ((res=match(ms, s, p)) == NULL)  /* match failed? */
8✔
311
    ms->level--;  /* undo capture */
×
312
  return res;
8✔
313
}
314

315
static const char *end_capture(MatchState *ms, const char *s,
16✔
316
                               const char *p)
317
{
318
  int l = capture_to_close(ms);
16✔
319
  const char *res;
16✔
320
  ms->capture[l].len = s - ms->capture[l].init;  /* close capture */
16✔
321
  if ((res = match(ms, s, p)) == NULL)  /* match failed? */
16✔
322
    ms->capture[l].len = CAP_UNFINISHED;  /* undo capture */
8✔
323
  return res;
16✔
324
}
325

326
static const char *match_capture(MatchState *ms, const char *s, int l)
×
327
{
328
  size_t len;
×
329
  l = check_capture(ms, l);
×
330
  len = (size_t)ms->capture[l].len;
×
331
  if ((size_t)(ms->src_end-s) >= len &&
×
332
      memcmp(ms->capture[l].init, s, len) == 0)
×
333
    return s+len;
×
334
  else
335
    return NULL;
336
}
337

338
static const char *match(MatchState *ms, const char *s, const char *p)
7,347✔
339
{
340
  if (++ms->depth > LJ_MAX_XLEVEL)
7,347✔
341
    lj_err_caller(ms->L, LJ_ERR_STRPATX);
×
342
  init: /* using goto's to optimize tail recursion */
7,347✔
343
  switch (*p) {
10,180✔
344
  case '(':  /* start capture */
8✔
345
    if (*(p+1) == ')')  /* position capture? */
8✔
346
      s = start_capture(ms, s, p+2, CAP_POSITION);
×
347
    else
348
      s = start_capture(ms, s, p+1, CAP_UNFINISHED);
8✔
349
    break;
350
  case ')':  /* end capture */
16✔
351
    s = end_capture(ms, s, p+1);
16✔
352
    break;
16✔
353
  case L_ESC:
254✔
354
    switch (*(p+1)) {
254✔
355
    case 'b':  /* balanced string? */
×
356
      s = matchbalance(ms, s, p+2);
×
357
      if (s == NULL) break;
×
358
      p+=4;
×
359
      goto init;  /* else s = match(ms, s, p+4); */
×
360
    case 'f': {  /* frontier? */
×
361
      const char *ep; char previous;
×
362
      p += 2;
×
363
      if (*p != '[')
×
364
        lj_err_caller(ms->L, LJ_ERR_STRPATB);
×
365
      ep = classend(ms, p);  /* points to what is next */
×
366
      previous = (s == ms->src_init) ? '\0' : *(s-1);
×
367
      if (matchbracketclass(uchar(previous), p, ep-1) ||
×
368
         !matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; }
×
369
      p=ep;
×
370
      goto init;  /* else s = match(ms, s, ep); */
×
371
      }
372
    default:
254✔
373
      if (lj_char_isdigit(uchar(*(p+1)))) {  /* capture results (%0-%9)? */
254✔
374
        s = match_capture(ms, s, uchar(*(p+1)));
×
375
        if (s == NULL) break;
×
376
        p+=2;
×
377
        goto init;  /* else s = match(ms, s, p+2) */
×
378
      }
379
      goto dflt;  /* case default */
254✔
380
    }
381
    break;
×
382
  case '\0':  /* end of pattern */
383
    break;  /* match succeeded */
384
  case '$':
5,329✔
385
    /* is the `$' the last char in pattern? */
386
    if (*(p+1) != '\0') goto dflt;
5,329✔
387
    if (s != ms->src_end) s = NULL;  /* check end of string */
5,329✔
388
    break;
389
  default: dflt: {  /* it is a pattern item */
4,733✔
390
    const char *ep = classend(ms, p);  /* points to what is next */
4,733✔
391
    int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
4,733✔
392
    switch (*ep) {
4,733✔
393
    case '?': {  /* optional */
4✔
394
      const char *res;
4✔
395
      if (m && ((res=match(ms, s+1, ep+1)) != NULL)) {
4✔
396
        s = res;
397
        break;
398
      }
399
      p=ep+1;
×
400
      goto init;  /* else s = match(ms, s, ep+1); */
×
401
      }
402
    case '*':  /* 0 or more repetitions */
4✔
403
      s = max_expand(ms, s, p, ep);
4✔
404
      break;
4✔
405
    case '+':  /* 1 or more repetitions */
1,241✔
406
      s = (m ? max_expand(ms, s+1, p, ep) : NULL);
1,241✔
407
      break;
408
    case '-':  /* 0 or more repetitions (minimum) */
7✔
409
      s = min_expand(ms, s, p, ep);
7✔
410
      break;
7✔
411
    default:
3,477✔
412
      if (m) { s++; p=ep; goto init; }  /* else s = match(ms, s+1, ep); */
3,477✔
413
      s = NULL;
414
      break;
415
    }
416
    break;
417
    }
418
  }
419
  ms->depth--;
7,347✔
420
  return s;
7,347✔
421
}
422

423
static void push_onecapture(MatchState *ms, int i, const char *s, const char *e)
109✔
424
{
425
  if (i >= ms->level) {
109✔
426
    if (i == 0)  /* ms->level == 0, too */
101✔
427
      lua_pushlstring(ms->L, s, (size_t)(e - s));  /* add whole match */
101✔
428
    else
429
      lj_err_caller(ms->L, LJ_ERR_STRCAPI);
×
430
  } else {
431
    ptrdiff_t l = ms->capture[i].len;
8✔
432
    if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU);
8✔
433
    if (l == CAP_POSITION)
8✔
434
      lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
×
435
    else
436
      lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l);
8✔
437
  }
438
}
109✔
439

440
static int push_captures(MatchState *ms, const char *s, const char *e)
109✔
441
{
442
  int i;
109✔
443
  int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
109✔
444
  luaL_checkstack(ms->L, nlevels, "too many captures");
109✔
445
  for (i = 0; i < nlevels; i++)
327✔
446
    push_onecapture(ms, i, s, e);
109✔
447
  return nlevels;  /* number of strings pushed */
109✔
448
}
449

450
static int str_find_aux(lua_State *L, int find)
113✔
451
{
452
  GCstr *s = lj_lib_checkstr(L, 1);
113✔
453
  GCstr *p = lj_lib_checkstr(L, 2);
113✔
454
  int32_t start = lj_lib_optint(L, 3, 1);
113✔
455
  MSize st;
113✔
456
  if (start < 0) start += (int32_t)s->len; else start--;
113✔
457
  if (start < 0) start = 0;
113✔
458
  st = (MSize)start;
113✔
459
  if (st > s->len) {
113✔
460
#if LJ_52
461
    setnilV(L->top-1);
462
    return 1;
463
#else
464
    st = s->len;
465
#endif
466
  }
467
  if (find && ((L->base+3 < L->top && tvistruecond(L->base+3)) ||
113✔
468
               !lj_str_haspattern(p))) {  /* Search for fixed string. */
×
469
    const char *q = lj_str_find(strdata(s)+st, strdata(p), s->len-st, p->len);
×
470
    if (q) {
×
471
      setintV(L->top-2, (int32_t)(q-strdata(s)) + 1);
×
472
      setintV(L->top-1, (int32_t)(q-strdata(s)) + (int32_t)p->len);
×
473
      return 2;
×
474
    }
475
  } else {  /* Search for pattern. */
476
    MatchState ms;
113✔
477
    const char *pstr = strdata(p);
113✔
478
    const char *sstr = strdata(s) + st;
113✔
479
    int anchor = 0;
113✔
480
    if (*pstr == '^') { pstr++; anchor = 1; }
113✔
481
    ms.L = L;
113✔
482
    ms.src_init = strdata(s);
113✔
483
    ms.src_end = strdata(s) + s->len;
113✔
484
    do {  /* Loop through string and try to match the pattern. */
626✔
485
      const char *q;
626✔
486
      ms.level = ms.depth = 0;
626✔
487
      q = match(&ms, sstr, pstr);
626✔
488
      if (q) {
626✔
489
        if (find) {
109✔
490
          setintV(L->top++, (int32_t)(sstr-(strdata(s)-1)));
×
491
          setintV(L->top++, (int32_t)(q-strdata(s)));
×
492
          return push_captures(&ms, NULL, NULL) + 2;
109✔
493
        } else {
494
          return push_captures(&ms, sstr, q);
109✔
495
        }
496
      }
497
    } while (sstr++ < ms.src_end && !anchor);
517✔
498
  }
499
  setnilV(L->top-1);  /* Not found. */
4✔
500
  return 1;
4✔
501
}
502

503
LJLIB_CF(string_find)                LJLIB_REC(.)
×
504
{
505
  return str_find_aux(L, 1);
×
506
}
507

508
LJLIB_CF(string_match)
113✔
509
{
510
  return str_find_aux(L, 0);
113✔
511
}
512

513
LJLIB_NOREG LJLIB_CF(string_gmatch_aux)
×
514
{
515
  const char *p = strVdata(lj_lib_upvalue(L, 2));
×
516
  GCstr *str = strV(lj_lib_upvalue(L, 1));
×
517
  const char *s = strdata(str);
×
518
  TValue *tvpos = lj_lib_upvalue(L, 3);
×
519
  const char *src = s + tvpos->u32.lo;
×
520
  MatchState ms;
×
521
  ms.L = L;
×
522
  ms.src_init = s;
×
523
  ms.src_end = s + str->len;
×
524
  for (; src <= ms.src_end; src++) {
×
525
    const char *e;
×
526
    ms.level = ms.depth = 0;
×
527
    if ((e = match(&ms, src, p)) != NULL) {
×
528
      int32_t pos = (int32_t)(e - s);
×
529
      if (e == src) pos++;  /* Ensure progress for empty match. */
×
530
      tvpos->u32.lo = (uint32_t)pos;
×
531
      return push_captures(&ms, src, e);
×
532
    }
533
  }
534
  return 0;  /* not found */
535
}
536

537
LJLIB_CF(string_gmatch)
×
538
{
539
  lj_lib_checkstr(L, 1);
×
540
  lj_lib_checkstr(L, 2);
×
541
  L->top = L->base+3;
×
542
  (L->top-1)->u64 = 0;
×
543
  lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3);
×
544
  return 1;
×
545
}
546

547
static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
18✔
548
{
549
  size_t l, i;
18✔
550
  const char *news = lua_tolstring(ms->L, 3, &l);
18✔
551
  for (i = 0; i < l; i++) {
38✔
552
    if (news[i] != L_ESC) {
2✔
553
      luaL_addchar(b, news[i]);
2✔
554
    } else {
555
      i++;  /* skip ESC */
×
556
      if (!lj_char_isdigit(uchar(news[i]))) {
×
557
        luaL_addchar(b, news[i]);
×
558
      } else if (news[i] == '0') {
×
559
        luaL_addlstring(b, s, (size_t)(e - s));
×
560
      } else {
561
        push_onecapture(ms, news[i] - '1', s, e);
×
562
        luaL_addvalue(b);  /* add capture to accumulated result */
×
563
      }
564
    }
565
  }
566
}
18✔
567

568
static void add_value(MatchState *ms, luaL_Buffer *b,
18✔
569
                      const char *s, const char *e)
570
{
571
  lua_State *L = ms->L;
18✔
572
  switch (lua_type(L, 3)) {
18✔
573
    case LUA_TNUMBER:
18✔
574
    case LUA_TSTRING: {
575
      add_s(ms, b, s, e);
18✔
576
      return;
18✔
577
    }
578
    case LUA_TFUNCTION: {
×
579
      int n;
×
580
      lua_pushvalue(L, 3);
×
581
      n = push_captures(ms, s, e);
×
582
      lua_call(L, n, 1);
×
583
      break;
×
584
    }
585
    case LUA_TTABLE: {
×
586
      push_onecapture(ms, 0, s, e);
×
587
      lua_gettable(L, 3);
×
588
      break;
×
589
    }
590
  }
591
  if (!lua_toboolean(L, -1)) {  /* nil or false? */
×
592
    lua_pop(L, 1);
×
593
    lua_pushlstring(L, s, (size_t)(e - s));  /* keep original text */
×
594
  } else if (!lua_isstring(L, -1)) {
×
595
    lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
×
596
  }
597
  luaL_addvalue(b);  /* add result to accumulator */
×
598
}
599

600
LJLIB_CF(string_gsub)
22✔
601
{
602
  size_t srcl;
22✔
603
  const char *src = luaL_checklstring(L, 1, &srcl);
22✔
604
  const char *p = luaL_checkstring(L, 2);
22✔
605
  int  tr = lua_type(L, 3);
22✔
606
  int max_s = luaL_optint(L, 4, (int)(srcl+1));
22✔
607
  int anchor = (*p == '^') ? (p++, 1) : 0;
22✔
608
  int n = 0;
22✔
609
  MatchState ms;
22✔
610
  luaL_Buffer b;
22✔
611
  if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
×
612
        tr == LUA_TFUNCTION || tr == LUA_TTABLE))
22✔
613
    lj_err_arg(L, 3, LJ_ERR_NOSFT);
×
614
  luaL_buffinit(L, &b);
22✔
615
  ms.L = L;
22✔
616
  ms.src_init = src;
22✔
617
  ms.src_end = src+srcl;
22✔
618
  while (n < max_s) {
1,096✔
619
    const char *e;
1,096✔
620
    ms.level = ms.depth = 0;
1,096✔
621
    e = match(&ms, src, p);
1,096✔
622
    if (e) {
1,096✔
623
      n++;
18✔
624
      add_value(&ms, &b, src, e);
18✔
625
    }
626
    if (e && e>src) /* non empty match? */
1,096✔
627
      src = e;  /* skip it */
628
    else if (src < ms.src_end)
1,078✔
629
      luaL_addchar(&b, *src++);
1,056✔
630
    else
631
      break;
632
    if (anchor)
1,074✔
633
      break;
634
  }
635
  luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
22✔
636
  luaL_pushresult(&b);
22✔
637
  lua_pushinteger(L, n);  /* number of substitutions */
22✔
638
  return 2;
22✔
639
}
640

641
/* ------------------------------------------------------------------------ */
642

643
/* Emulate tostring() inline. */
644
static GCstr *string_fmt_tostring(lua_State *L, int arg, int retry)
48✔
645
{
646
  TValue *o = L->base+arg-1;
48✔
647
  cTValue *mo;
48✔
648
  lua_assert(o < L->top);  /* Caller already checks for existence. */
48✔
649
  if (LJ_LIKELY(tvisstr(o)))
48✔
650
    return strV(o);
40✔
651
  if (retry != 2 && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
8✔
652
    copyTV(L, L->top++, mo);
×
653
    copyTV(L, L->top++, o);
×
654
    lua_call(L, 1, 1);
×
655
    copyTV(L, L->base+arg-1, --L->top);
×
656
    return NULL;  /* Buffer may be overwritten, retry. */
×
657
  }
658
  return lj_strfmt_obj(L, o);
8✔
659
}
660

661
LJLIB_CF(string_format)                LJLIB_REC(.)
25✔
662
{
663
  int arg, top = (int)(L->top - L->base);
25✔
664
  GCstr *fmt;
25✔
665
  SBuf *sb;
25✔
666
  FormatState fs;
25✔
667
  SFormat sf;
25✔
668
  int retry = 0;
25✔
669
again:
25✔
670
  arg = 1;
25✔
671
  sb = lj_buf_tmp_(L);
25✔
672
  fmt = lj_lib_checkstr(L, arg);
25✔
673
  lj_strfmt_init(&fs, strdata(fmt), fmt->len);
25✔
674
  while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {
161✔
675
    if (sf == STRFMT_LIT) {
136✔
676
      lj_buf_putmem(sb, fs.str, fs.len);
66✔
677
    } else if (sf == STRFMT_ERR) {
70✔
678
      lj_err_callerv(L, LJ_ERR_STRFMT, strdata(lj_str_new(L, fs.str, fs.len)));
×
679
    } else {
680
      if (++arg > top)
70✔
681
        luaL_argerror(L, arg, lj_obj_typename[0]);
×
682
      switch (STRFMT_TYPE(sf)) {
70✔
683
      case STRFMT_INT:
684
        if (tvisint(L->base+arg-1)) {
22✔
685
          int32_t k = intV(L->base+arg-1);
686
          if (sf == STRFMT_INT)
687
            lj_strfmt_putint(sb, k);  /* Shortcut for plain %d. */
688
          else
689
            lj_strfmt_putfxint(sb, sf, k);
690
        } else {
691
          lj_strfmt_putfnum_int(sb, sf, lj_lib_checknum(L, arg));
22✔
692
        }
693
        break;
22✔
694
      case STRFMT_UINT:
695
        if (tvisint(L->base+arg-1))
×
696
          lj_strfmt_putfxint(sb, sf, intV(L->base+arg-1));
697
        else
698
          lj_strfmt_putfnum_uint(sb, sf, lj_lib_checknum(L, arg));
×
699
        break;
×
700
      case STRFMT_NUM:
×
701
        lj_strfmt_putfnum(sb, sf, lj_lib_checknum(L, arg));
×
702
        break;
×
703
      case STRFMT_STR: {
48✔
704
        GCstr *str = string_fmt_tostring(L, arg, retry);
48✔
705
        if (str == NULL)
48✔
706
          retry = 1;
707
        else if ((sf & STRFMT_T_QUOTED))
48✔
708
          lj_strfmt_putquoted(sb, str);  /* No formatting. */
×
709
        else
710
          lj_strfmt_putfstr(sb, sf, str);
48✔
711
        break;
712
        }
713
      case STRFMT_CHAR:
×
714
        lj_strfmt_putfchar(sb, sf, lj_lib_checkint(L, arg));
×
715
        break;
×
716
      case STRFMT_PTR:  /* No formatting. */
×
717
        lj_strfmt_putptr(sb, lj_obj_ptr(G(L), L->base+arg-1));
×
718
        break;
×
719
      default:
720
        lua_assert(0);
721
        break;
722
      }
723
    }
724
  }
725
  if (retry++ == 1) goto again;
25✔
726
  setstrV(L, L->top-1, lj_buf_str(L, sb));
25✔
727
  lj_gc_check(L);
25✔
728
  return 1;
25✔
729
}
730

731
/* ------------------------------------------------------------------------ */
732

733
#include "lj_libdef.h"
734

735
LUALIB_API int luaopen_string(lua_State *L)
55✔
736
{
737
  GCtab *mt;
55✔
738
  global_State *g;
55✔
739
  LJ_LIB_REG(L, LUA_STRLIBNAME, string);
55✔
740
  mt = lj_tab_new(L, 0, 1);
55✔
741
  /* NOBARRIER: basemt is a GC root. */
742
  g = G(L);
55✔
743
  setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt));
55✔
744
  settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1));
55✔
745
  mt->nomm = (uint8_t)(~(1u<<MM_index));
55✔
746
  return 1;
55✔
747
}
748

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