/* Custom string builder, should be slightly faster than using Sv* macros directly. */ typedef struct { SV *sv; char *cur; char *end; size_t maxlen; } fustr; /* sv must be a new SV with a preallocated buffer */ static void fustr_init_(pTHX_ fustr *s, SV *sv, size_t maxlen) { s->sv = sv; SvPOK_only(s->sv); s->cur = SvPVX(s->sv); s->end = SvEND(s->sv); s->maxlen = maxlen; } static void fustr_grow(pTHX_ fustr *s, size_t add) { size_t off = s->cur - SvPVX(s->sv); size_t newlen = 64; add += off; if (add > s->maxlen) croak("maximum string length exceeded"); /* Increment to next power of two; SvGROW's default strategy is slow */ while (newlen < add) newlen <<= 1; if (newlen > s->maxlen) newlen = s->maxlen; char *buf = SvGROW(s->sv, newlen); s->cur = buf + off; s->end = buf + (SvLEN(s->sv) > s->maxlen ? s->maxlen : SvLEN(s->sv)); } static inline void fustr_reserve_(pTHX_ fustr *s, size_t add) { if (UNLIKELY(s->end < s->cur + add)) fustr_grow(aTHX_ s, add); } static inline void fustr_write_(pTHX_ fustr *s, const char *str, size_t n) { fustr_reserve_(aTHX_ s, n); memcpy(s->cur, str, n); s->cur += n; } /* Adds n uninitialized bytes to the string and returns a buffer to write the data to */ static inline char *fustr_write_buf_(pTHX_ fustr *s, size_t n) { fustr_reserve_(aTHX_ s, n); char *buf = s->cur; s->cur += n; return buf; } static SV *fustr_done_(pTHX_ fustr *s) { fustr_reserve_(aTHX_ s, 1); *s->cur = 0; SvCUR_set(s->sv, s->cur - SvPVX(s->sv)); // TODO: SvPV_shrink_to_cur? return s->sv; } #define fustr_init(a,b,c) fustr_init_(aTHX_ a,b,c) #define fustr_reserve(a,b) fustr_reserve_(aTHX_ a,b) #define fustr_write(a,b,c) fustr_write_(aTHX_ a,b,c) #define fustr_write_buf(a,b) fustr_write_buf_(aTHX_ a,b) #define fustr_done(a) fustr_done_(aTHX_ a)