return FFH_RETRY;
}
-LJLIB_ASM(string_rep)
+LJLIB_CF(string_rep)
{
GCstr *s = lj_lib_checkstr(L, 1);
- int32_t k = lj_lib_checkint(L, 2);
+ int32_t rep = lj_lib_checkint(L, 2);
GCstr *sep = lj_lib_optstr(L, 3);
- int32_t len = (int32_t)s->len;
- global_State *g = G(L);
- int64_t tlen;
- if (k <= 0) {
- empty:
- setstrV(L, L->base-1, &g->strempty);
- return FFH_RES(1);
- }
- if (sep) {
- tlen = (int64_t)len + sep->len;
- if (tlen > LJ_MAX_STR)
- lj_err_caller(L, LJ_ERR_STROV);
- tlen *= k;
- if (tlen > LJ_MAX_STR)
- lj_err_caller(L, LJ_ERR_STROV);
- } else {
- tlen = (int64_t)k * len;
- if (tlen > LJ_MAX_STR)
- lj_err_caller(L, LJ_ERR_STROV);
- }
- if (tlen == 0) {
- goto empty;
- } else {
- char *buf = lj_buf_tmp(L, (MSize)tlen), *p = buf;
- const char *src = strdata(s);
- if (sep) {
- tlen -= sep->len; /* Ignore trailing separator. */
- if (k > 1) { /* Paste one string and one separator. */
- int32_t i;
- i = 0; while (i < len) *p++ = src[i++];
- src = strdata(sep); len = sep->len;
- i = 0; while (i < len) *p++ = src[i++];
- src = buf; len += s->len; k--; /* Now copy that k-1 times. */
- }
- }
- do {
- int32_t i = 0;
- do { *p++ = src[i++]; } while (i < len);
- } while (--k > 0);
- setstrV(L, L->base-1, lj_str_new(L, buf, (size_t)tlen));
+ SBuf *sb = lj_buf_tmp_(L);
+ if (sep && rep > 1) {
+ GCstr *s2 = lj_buf_cat2str(L, sep, s);
+ lj_buf_reset(sb);
+ lj_buf_putstr(sb, s);
+ s = s2;
+ rep--;
}
- return FFH_RES(1);
+ sb = lj_buf_putstr_rep(sb, s, rep);
+ setstrV(L, L->top-1, lj_buf_str(L, sb));
+ lj_gc_check(L);
+ return 1;
}
LJLIB_ASM(string_reverse) LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse)
return sb;
}
+SBuf *lj_buf_putstr_rep(SBuf *sb, GCstr *s, int32_t rep)
+{
+ MSize len = s->len;
+ if (rep > 0 && len) {
+ uint64_t tlen = (uint64_t)rep * len;
+ char *p;
+ if (LJ_UNLIKELY(tlen > LJ_MAX_STR))
+ lj_err_mem(sbufL(sb));
+ p = lj_buf_more(sb, (MSize)tlen);
+ if (len == 1) { /* Optimize a common case. */
+ uint32_t c = strdata(s)[0];
+ do { *p++ = c; } while (--rep > 0);
+ } else {
+ const char *e = strdata(s) + len;
+ do {
+ const char *q = strdata(s);
+ do { *p++ = *q++; } while (q < e);
+ } while (--rep > 0);
+ }
+ setsbufP(sb, p);
+ }
+ return sb;
+}
+
GCstr * LJ_FASTCALL lj_buf_tostr(SBuf *sb)
{
return lj_str_new(sbufL(sb), sbufB(sb), sbuflen(sb));
LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_reverse(SBuf *sb, GCstr *s);
LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_lower(SBuf *sb, GCstr *s);
LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_upper(SBuf *sb, GCstr *s);
+LJ_FUNC SBuf *lj_buf_putstr_rep(SBuf *sb, GCstr *s, int32_t rep);
LJ_FUNCA GCstr * LJ_FASTCALL lj_buf_tostr(SBuf *sb);
LJ_FUNC GCstr *lj_buf_cat2str(lua_State *L, GCstr *s1, GCstr *s2);
LJ_FUNC uint32_t LJ_FASTCALL lj_buf_ruleb128(const char **pp);
#define HOTCOUNT_CALL 1
/* This solves a circular dependency problem -- bump as needed. Sigh. */
-#define GG_NUM_ASMFF 58
+#define GG_NUM_ASMFF 57
#define GG_LEN_DDISP (BC__MAX + GG_NUM_ASMFF)
#define GG_LEN_SDISP BC_FUNCF
| mvn CARG2, #~LJ_TSTR
| b ->fff_restv
|
- |.ffunc string_rep // Only handle the 1-char case inline.
- | ffgccheck
- | ldrd CARG12, [BASE]
- | ldrd CARG34, [BASE, #8]
- | cmp NARGS8:RC, #16
- | bne ->fff_fallback // Exactly 2 arguments
- | checktp CARG2, LJ_TSTR
- | checktpeq CARG4, LJ_TISNUM
- | bne ->fff_fallback
- | subs CARG4, CARG3, #1
- | ldr CARG2, STR:CARG1->len
- | blt ->fff_emptystr // Count <= 0?
- | cmp CARG2, #1
- | blo ->fff_emptystr // Zero-length string?
- | bne ->fff_fallback // Fallback for > 1-char strings.
- | ldr CARG2, [DISPATCH, #DISPATCH_GL(tmpbuf.b)]
- | ldr RB, [DISPATCH, #DISPATCH_GL(tmpbuf.e)]
- | ldr CARG1, STR:CARG1[1]
- | add INS, CARG2, CARG3
- | cmp RB, INS
- | blo ->fff_fallback
- |1: // Fill buffer with char.
- | strb CARG1, [CARG2, CARG4]
- | subs CARG4, CARG4, #1
- | bge <1
- | b ->fff_newstr
- |
|.macro ffstring_op, name
| .ffunc string_ .. name
| ffgccheck
| b ->fff_restv
|. li CARG3, LJ_TSTR
|
- |.ffunc string_rep // Only handle the 1-char case inline.
- | ffgccheck
- | lw TMP0, HI(BASE)
- | addiu AT, NARGS8:RC, -16 // Exactly 2 arguments.
- | lw CARG4, 8+HI(BASE)
- | lw STR:CARG1, LO(BASE)
- | addiu TMP0, TMP0, -LJ_TSTR
- | ldc1 f0, 8(BASE)
- | or AT, AT, TMP0
- | bnez AT, ->fff_fallback
- |. sltiu AT, CARG4, LJ_TISNUM
- | trunc.w.d f0, f0
- | beqz AT, ->fff_fallback
- |. lw TMP0, STR:CARG1->len
- | mfc1 CARG3, f0
- | lw CARG2, DISPATCH_GL(tmpbuf.b)(DISPATCH)
- | lw TMP1, DISPATCH_GL(tmpbuf.e)(DISPATCH)
- | li AT, 1
- | blez CARG3, ->fff_emptystr // Count <= 0?
- |. sltu AT, AT, TMP0
- | beqz TMP0, ->fff_emptystr // Zero length string?
- |. addu TMP3, CARG2, CARG3
- | sltu TMP0, TMP1, TMP3
- | or AT, AT, TMP0
- | bnez AT, ->fff_fallback // Fallback for > 1-char strings.
- |. lbu TMP0, STR:CARG1[1]
- | addu TMP2, CARG2, CARG3
- |1: // Fill buffer with char. Yes, this is suboptimal code (do you care?).
- | addiu TMP2, TMP2, -1
- | sltu AT, CARG2, TMP2
- | bnez AT, <1
- |. sb TMP0, 0(TMP2)
- | b ->fff_newstr
- |. nop
- |
|.macro ffstring_op, name
| .ffunc string_ .. name
| ffgccheck
| addi TMP1, TMP1, 1 // start = 1 + (start ? start+len : 0)
| b <3
|
- |.ffunc string_rep // Only handle the 1-char case inline.
- | ffgccheck
- | cmplwi NARGS8:RC, 16
- | lwz TMP0, 0(BASE)
- | lwz STR:CARG1, 4(BASE)
- | lwz CARG4, 8(BASE)
- |.if DUALNUM
- | lwz CARG3, 12(BASE)
- |.else
- | lfd FARG2, 8(BASE)
- |.endif
- | bne ->fff_fallback // Exactly 2 arguments.
- | checkstr TMP0; bne ->fff_fallback
- |.if DUALNUM
- | checknum CARG4; bne ->fff_fallback
- |.else
- | checknum CARG4; bge ->fff_fallback
- | toint CARG3, FARG2
- |.endif
- | lwz TMP0, STR:CARG1->len
- | cmpwi CARG3, 0
- | lwz TMP1, DISPATCH_GL(tmpbuf.e)(DISPATCH)
- | lwz CARG2, DISPATCH_GL(tmpbuf.b)(DISPATCH)
- | ble >2 // Count <= 0? (or non-int)
- | cmplwi TMP0, 1
- | add TMP3, CARG2, CARG3
- | subi TMP2, CARG3, 1
- | blt >2 // Zero length string?
- | cmplw cr1, TMP1, TMP3
- | bne ->fff_fallback // Fallback for > 1-char strings.
- | lbz TMP0, STR:CARG1[1]
- | blt cr1, ->fff_fallback
- |1: // Fill buffer with char. Yes, this is suboptimal code (do you care?).
- | cmplwi TMP2, 0
- | stbx TMP0, CARG2, TMP2
- | subi TMP2, TMP2, 1
- | bne <1
- | b ->fff_newstr
- |2: // Return empty string.
- | la STR:CARG1, DISPATCH_GL(strempty)(DISPATCH)
- | li CARG3, LJ_TSTR
- | b ->fff_restv
- |
|.macro ffstring_op, name
| .ffunc string_ .. name
| ffgccheck
| xor RC, RC // Zero length. Any ptr in RB is ok.
| jmp <4
|
- |.ffunc string_rep // Only handle the 1-char case inline.
- | ffgccheck
- | cmp NARGS:RD, 2+1; jne ->fff_fallback // Exactly 2 arguments.
- | cmp dword [BASE+4], LJ_TSTR; jne ->fff_fallback
- | cmp dword [BASE+12], LJ_TISNUM
- | mov STR:RB, [BASE]
- |.if DUALNUM
- | jne ->fff_fallback
- | mov RC, dword [BASE+8]
- |.else
- | jae ->fff_fallback
- | cvttsd2si RC, qword [BASE+8]
- |.endif
- | test RC, RC
- | jle ->fff_emptystr // Count <= 0? (or non-int)
- | cmp dword STR:RB->len, 1
- | jb ->fff_emptystr // Zero length string?
- | jne ->fff_fallback_2 // Fallback for > 1-char strings.
- | movzx RA, byte STR:RB[1]
- | mov RB, [DISPATCH+DISPATCH_GL(tmpbuf.b)]
- | add RB, RC
- | cmp [DISPATCH+DISPATCH_GL(tmpbuf.e)], RB; jb ->fff_fallback_2
- |.if X64
- | mov TMP3, RC
- |.else
- | mov ARG3, RC
- |.endif
- |1: // Fill buffer with char.
- | sub RB, 1
- | sub RC, 1
- | mov [RB], RAL
- | jnz <1
- | mov RD, [DISPATCH+DISPATCH_GL(tmpbuf.b)]
- | jmp ->fff_newstr
- |
|.macro ffstring_op, name
| .ffunc_1 string_ .. name
| ffgccheck