Unverified Commit e9877bc7 authored by Yukihiro "Matz" Matsumoto's avatar Yukihiro "Matz" Matsumoto Committed by GitHub

Merge pull request #4970 from dearblue/nostdio-sprintf

 Support `MRB_DISABLE_STDIO` for mruby-sprintf; ref #4954
parents ea632ab8 191ccbf6
...@@ -37,6 +37,7 @@ MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base); ...@@ -37,6 +37,7 @@ MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base);
/* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */ /* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */
#ifndef MRB_WITHOUT_FLOAT #ifndef MRB_WITHOUT_FLOAT
MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt); MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt);
MRB_API int mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float f);
MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x); MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x);
MRB_API mrb_value mrb_int_value(mrb_state *mrb, mrb_float f); MRB_API mrb_value mrb_int_value(mrb_state *mrb, mrb_float f);
#endif #endif
......
...@@ -5,11 +5,6 @@ ...@@ -5,11 +5,6 @@
*/ */
#include <mruby.h> #include <mruby.h>
#ifdef MRB_DISABLE_STDIO
# error sprintf conflicts 'MRB_DISABLE_STDIO' configuration in your 'build_config.rb'
#endif
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <mruby/string.h> #include <mruby/string.h>
...@@ -521,6 +516,50 @@ mrb_f_sprintf(mrb_state *mrb, mrb_value obj) ...@@ -521,6 +516,50 @@ mrb_f_sprintf(mrb_state *mrb, mrb_value obj)
} }
} }
static int
mrb_int2str(char *buf, size_t len, mrb_int n)
{
#ifdef MRB_DISABLE_STDIO
char *bufend = buf + len;
char *p = bufend - 1;
if (len < 1) return -1;
*p -- = '\0';
len --;
if (n < 0) {
if (len < 1) return -1;
*p -- = '-';
len --;
n = -n;
}
if (n > 0) {
for (; n > 0; len --, n /= 10) {
if (len < 1) return -1;
*p -- = '0' + (n % 10);
}
p ++;
}
else if (len > 0) {
*p = '0';
len --;
}
else {
return -1;
}
memmove(buf, p, bufend - p);
return bufend - p - 1;
#else
return snprintf(buf, len, "%" MRB_PRId, n);
#endif /* MRB_DISABLE_STDIO */
}
mrb_value mrb_value
mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt) mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt)
{ {
...@@ -869,7 +908,7 @@ retry: ...@@ -869,7 +908,7 @@ retry:
width--; width--;
} }
mrb_assert(base == 10); mrb_assert(base == 10);
snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); mrb_int2str(nbuf, sizeof(nbuf), v);
s = nbuf; s = nbuf;
if (v < 0) s++; /* skip minus sign */ if (v < 0) s++; /* skip minus sign */
} }
...@@ -877,24 +916,12 @@ retry: ...@@ -877,24 +916,12 @@ retry:
s = nbuf; s = nbuf;
if (v < 0) { if (v < 0) {
dots = 1; dots = 1;
}
switch (base) {
case 2:
if (v < 0) {
val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
} }
else { else {
val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
} }
strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1);
break;
case 8:
snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v);
break;
case 16:
snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v);
break;
}
if (v < 0) { if (v < 0) {
char d; char d;
...@@ -1071,7 +1098,7 @@ retry: ...@@ -1071,7 +1098,7 @@ retry:
need += 20; need += 20;
CHECK(need); CHECK(need);
n = snprintf(&buf[blen], need, fbuf, fval); n = mrb_float_to_cstr(mrb, &buf[blen], need, fbuf, fval);
if (n < 0 || n >= need) { if (n < 0 || n >= need) {
mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error"); mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
} }
...@@ -1113,12 +1140,13 @@ fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec) ...@@ -1113,12 +1140,13 @@ fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec)
if (flags & FSPACE) *buf++ = ' '; if (flags & FSPACE) *buf++ = ' ';
if (flags & FWIDTH) { if (flags & FWIDTH) {
n = snprintf(buf, end - buf, "%d", (int)width); n = mrb_int2str(buf, end - buf, width);
buf += n; buf += n;
} }
if (flags & FPREC) { if (flags & FPREC) {
n = snprintf(buf, end - buf, ".%d", (int)prec); *buf ++ = '.';
n = mrb_int2str(buf, end - buf, prec);
buf += n; buf += n;
} }
......
...@@ -8,6 +8,11 @@ assert('String#%') do ...@@ -8,6 +8,11 @@ assert('String#%') do
assert_equal 15, ("%b" % (1<<14)).size assert_equal 15, ("%b" % (1<<14)).size
skip unless Object.const_defined?(:Float) skip unless Object.const_defined?(:Float)
assert_equal "1.0", "%3.1f" % 1.01 assert_equal "1.0", "%3.1f" % 1.01
assert_equal " 123456789.12", "% 4.2f" % 123456789.123456789
assert_equal "123456789.12", "%-4.2f" % 123456789.123456789
assert_equal "+123456789.12", "%+4.2f" % 123456789.123456789
assert_equal "123456789.12", "%04.2f" % 123456789.123456789
assert_equal "00000000123456789.12", "%020.2f" % 123456789.123456789
end end
assert('String#% with inf') do assert('String#% with inf') do
......
...@@ -37,9 +37,19 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -37,9 +37,19 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <mruby.h> #include <mruby.h>
#include <mruby/string.h> #include <mruby/string.h>
struct fmt_args;
typedef void output_func(struct fmt_args *f, const char *s, size_t l);
struct fmt_args { struct fmt_args {
mrb_state *mrb; mrb_state *mrb;
mrb_value str; output_func *output;
void *opaque;
};
struct mrb_cstr {
char *buf;
size_t len;
}; };
#define MAX(a,b) ((a)>(b) ? (a) : (b)) #define MAX(a,b) ((a)>(b) ? (a) : (b))
...@@ -54,15 +64,44 @@ struct fmt_args { ...@@ -54,15 +64,44 @@ struct fmt_args {
#define PAD_POS (1U<<(' '-' ')) #define PAD_POS (1U<<(' '-' '))
#define MARK_POS (1U<<('+'-' ')) #define MARK_POS (1U<<('+'-' '))
#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS)
static output_func strcat_value;
static output_func strcat_cstr;
static void
strcat_value(struct fmt_args *f, const char *s, size_t l)
{
mrb_value str = *(mrb_value*)f->opaque;
mrb_str_cat(f->mrb, str, s, l);
}
static void
strcat_cstr(struct fmt_args *f, const char *s, size_t l)
{
struct mrb_cstr *cstr = (struct mrb_cstr*)f->opaque;
if (l > cstr->len) {
mrb_state *mrb = f->mrb;
mrb_raise(mrb, E_ARGUMENT_ERROR, "string buffer too small");
}
memcpy(cstr->buf, s, l);
cstr->buf += l;
cstr->len -= l;
}
static void static void
out(struct fmt_args *f, const char *s, size_t l) out(struct fmt_args *f, const char *s, size_t l)
{ {
mrb_str_cat(f->mrb, f->str, s, l); f->output(f, s, l);
} }
#define PAD_SIZE 256 #define PAD_SIZE 256
static void static void
pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl) pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint32_t fl)
{ {
char pad[PAD_SIZE]; char pad[PAD_SIZE];
if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
...@@ -92,7 +131,7 @@ typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double) ...@@ -92,7 +131,7 @@ typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)
#endif #endif
static int static int
fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) fmt_fp(struct fmt_args *f, long double y, ptrdiff_t w, ptrdiff_t p, uint32_t fl, int t)
{ {
uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
+ (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
...@@ -117,11 +156,11 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) ...@@ -117,11 +156,11 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
if (!isfinite(y)) { if (!isfinite(y)) {
const char *ss = (t&32)?"inf":"INF"; const char *ss = (t&32)?"inf":"INF";
if (y!=y) ss=(t&32)?"nan":"NAN"; if (y!=y) ss=(t&32)?"nan":"NAN";
pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD); pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
out(f, prefix, pl); out(f, prefix, pl);
out(f, ss, 3); out(f, ss, 3);
pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ); pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
return 3+(int)pl; return MAX(w, 3+(int)pl);
} }
y = frexp((double)y, &e2) * 2; y = frexp((double)y, &e2) * 2;
...@@ -169,14 +208,14 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) ...@@ -169,14 +208,14 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
else else
l = (s-buf) + (ebuf-estr); l = (s-buf) + (ebuf-estr);
pad(f, ' ', 0, pl+l, fl); pad(f, ' ', w, pl+l, fl);
out(f, prefix, pl); out(f, prefix, pl);
pad(f, '0', 0, pl+l, fl^ZERO_PAD); pad(f, '0', w, pl+l, fl^ZERO_PAD);
out(f, buf, s-buf); out(f, buf, s-buf);
pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
out(f, estr, ebuf-estr); out(f, estr, ebuf-estr);
pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
return (int)pl+(int)l; return MAX(w, (int)pl+(int)l);
} }
if (p<0) p=6; if (p<0) p=6;
...@@ -288,9 +327,9 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) ...@@ -288,9 +327,9 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
l += ebuf-estr; l += ebuf-estr;
} }
pad(f, ' ', 0, pl+l, fl); pad(f, ' ', w, pl+l, fl);
out(f, prefix, pl); out(f, prefix, pl);
pad(f, '0', 0, pl+l, fl^ZERO_PAD); pad(f, '0', w, pl+l, fl^ZERO_PAD);
if ((t|32)=='f') { if ((t|32)=='f') {
if (a>r) a=r; if (a>r) a=r;
...@@ -325,21 +364,33 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) ...@@ -325,21 +364,33 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
out(f, estr, ebuf-estr); out(f, estr, ebuf-estr);
} }
pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
return (int)pl+(int)l; return MAX(w, (int)pl+(int)l);
} }
static int static int
fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
{ {
ptrdiff_t p; ptrdiff_t w, p;
uint32_t fl;
if (*fmt != '%') { if (*fmt != '%') {
return -1; return -1;
} }
++fmt; ++fmt;
/* Read modifier flags */
for (fl=0; (unsigned)*fmt-' '<32 && (FLAGMASK&(1U<<(*fmt-' '))); fmt++)
fl |= 1U<<(*fmt-' ');
/* - and 0 flags are mutually exclusive */
if (fl & LEFT_ADJ) fl &= ~ZERO_PAD;
for (w = 0; ISDIGIT(*fmt); ++fmt) {
w = 10 * w + (*fmt - '0');
}
if (*fmt == '.') { if (*fmt == '.') {
++fmt; ++fmt;
for (p = 0; ISDIGIT(*fmt); ++fmt) { for (p = 0; ISDIGIT(*fmt); ++fmt) {
...@@ -353,29 +404,49 @@ fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) ...@@ -353,29 +404,49 @@ fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
switch (*fmt) { switch (*fmt) {
case 'e': case 'f': case 'g': case 'a': case 'e': case 'f': case 'g': case 'a':
case 'E': case 'F': case 'G': case 'A': case 'E': case 'F': case 'G': case 'A':
return fmt_fp(f, flo, p, 0, *fmt); return fmt_fp(f, flo, w, p, fl, *fmt);
default: default:
return -1; return -1;
} }
} }
mrb_value MRB_API mrb_value
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
{ {
struct fmt_args f; struct fmt_args f;
mrb_value str = mrb_str_new_capa(mrb, 24);
f.mrb = mrb; f.mrb = mrb;
f.str = mrb_str_new_capa(mrb, 24); f.output = strcat_value;
f.opaque = (void*)&str;
if (fmt_core(&f, fmt, mrb_float(flo)) < 0) { if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string"); mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
} }
return f.str; return str;
}
MRB_API int
mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float fval)
{
struct fmt_args f;
struct mrb_cstr cstr;
cstr.buf = buf;
cstr.len = len - 1; /* reserve NUL terminator */
f.mrb = mrb;
f.output = strcat_cstr;
f.opaque = (void*)&cstr;
if (fmt_core(&f, fmt, fval) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
}
*cstr.buf = '\0';
return cstr.buf - buf;
} }
#else /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */ #else /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
#include <mruby.h> #include <mruby.h>
#include <stdio.h> #include <stdio.h>
mrb_value MRB_API mrb_value
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
{ {
char buf[25]; char buf[25];
...@@ -383,5 +454,11 @@ mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) ...@@ -383,5 +454,11 @@ mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
snprintf(buf, sizeof(buf), fmt, mrb_float(flo)); snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
return mrb_str_new_cstr(mrb, buf); return mrb_str_new_cstr(mrb, buf);
} }
MRB_API int
mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float fval)
{
return snprintf(buf, len, fmt, fval);
}
#endif /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */ #endif /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment