From ccd84851117dbd9da97845fa6accc7102e8a2b04 Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 11 Apr 2020 21:18:57 +0900 Subject: Add missing `MRB_API` --- src/fmt_fp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/fmt_fp.c b/src/fmt_fp.c index b77abe7b5..558976e63 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -359,7 +359,7 @@ fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) } } -mrb_value +MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) { struct fmt_args f; @@ -375,7 +375,7 @@ mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) #include #include -mrb_value +MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) { char buf[25]; -- cgit v1.2.3 From 8ce3664305cdd585a0c961f9146aa58c37806a70 Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 11 Apr 2020 20:28:59 +0900 Subject: Supports width specifier with `mrb_float_to_str()` Based on src/stdio/vfprintf.c in git://git.musl-libc.org/musl --- src/fmt_fp.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/fmt_fp.c b/src/fmt_fp.c index 558976e63..8b1b01559 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -92,7 +92,7 @@ typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double) #endif 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, uint8_t fl, int t) { uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion @@ -117,11 +117,11 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) if (!isfinite(y)) { const char *ss = (t&32)?"inf":"INF"; 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, ss, 3); - pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ); - return 3+(int)pl; + pad(f, ' ', w, 3+pl, fl^LEFT_ADJ); + return MAX(w, 3+(int)pl); } y = frexp((double)y, &e2) * 2; @@ -169,14 +169,14 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) else l = (s-buf) + (ebuf-estr); - pad(f, ' ', 0, pl+l, fl); + pad(f, ' ', w, pl+l, fl); 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); pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); out(f, estr, ebuf-estr); - pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); - return (int)pl+(int)l; + pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + return MAX(w, (int)pl+(int)l); } if (p<0) p=6; @@ -288,9 +288,9 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) l += ebuf-estr; } - pad(f, ' ', 0, pl+l, fl); + pad(f, ' ', w, pl+l, fl); 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 (a>r) a=r; @@ -325,21 +325,25 @@ fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) 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 fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) { - ptrdiff_t p; + ptrdiff_t w, p; if (*fmt != '%') { return -1; } ++fmt; + for (w = 0; ISDIGIT(*fmt); ++fmt) { + w = 10 * w + (*fmt - '0'); + } + if (*fmt == '.') { ++fmt; for (p = 0; ISDIGIT(*fmt); ++fmt) { @@ -353,7 +357,7 @@ fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) switch (*fmt) { 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, 0, *fmt); default: return -1; } -- cgit v1.2.3 From 2ceab2d27084f033538f2f817249a7979134c816 Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 11 Apr 2020 21:19:19 +0900 Subject: Supports some specifier flags with `mrb_float_to_str()` Changed to understand `#`, `0`, `-`, ` ` and `+`. Based on src/stdio/vfprintf.c in git://git.musl-libc.org/musl --- mrbgems/mruby-sprintf/test/sprintf.rb | 5 +++++ src/fmt_fp.c | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb index 137812ae7..24d01c9be 100644 --- a/mrbgems/mruby-sprintf/test/sprintf.rb +++ b/mrbgems/mruby-sprintf/test/sprintf.rb @@ -8,6 +8,11 @@ assert('String#%') do assert_equal 15, ("%b" % (1<<14)).size skip unless Object.const_defined?(:Float) 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 assert('String#% with inf') do diff --git a/src/fmt_fp.c b/src/fmt_fp.c index 8b1b01559..99ba1e5ff 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -54,6 +54,8 @@ struct fmt_args { #define PAD_POS (1U<<(' '-' ')) #define MARK_POS (1U<<('+'-' ')) +#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS) + static void out(struct fmt_args *f, const char *s, size_t l) { @@ -62,7 +64,7 @@ out(struct fmt_args *f, const char *s, size_t l) #define PAD_SIZE 256 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]; if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; @@ -92,7 +94,7 @@ typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double) #endif static int -fmt_fp(struct fmt_args *f, long double y, ptrdiff_t w, 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 + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion @@ -334,12 +336,20 @@ static int fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) { ptrdiff_t w, p; + uint32_t fl; if (*fmt != '%') { return -1; } ++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'); } @@ -357,7 +367,7 @@ fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) switch (*fmt) { case 'e': case 'f': case 'g': case 'a': case 'E': case 'F': case 'G': case 'A': - return fmt_fp(f, flo, w, p, 0, *fmt); + return fmt_fp(f, flo, w, p, fl, *fmt); default: return -1; } -- cgit v1.2.3 From d9832699085d60e21002e4ba76e3e3fd0480d9e8 Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 11 Apr 2020 20:30:03 +0900 Subject: Add `mrb_float_to_cstr()` MRB_API function In order to share the same operation with `mrb_float_to_str()`, the internal structure is modified. --- include/mruby/numeric.h | 1 + src/fmt_fp.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h index a176d96cd..06a33cc6f 100644 --- a/include/mruby/numeric.h +++ b/include/mruby/numeric.h @@ -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]/ */ #ifndef MRB_WITHOUT_FLOAT 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_value mrb_int_value(mrb_state *mrb, mrb_float f); #endif diff --git a/src/fmt_fp.c b/src/fmt_fp.c index 99ba1e5ff..9ae5dd177 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -37,9 +37,19 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +struct fmt_args; + +typedef void output_func(struct fmt_args *f, const char *s, size_t l); + struct fmt_args { 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)) @@ -56,10 +66,37 @@ struct fmt_args { #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 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 @@ -377,13 +414,33 @@ MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) { struct fmt_args f; + mrb_value str = mrb_str_new_capa(mrb, 24); 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) { 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 */ #include @@ -397,5 +454,11 @@ mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) snprintf(buf, sizeof(buf), fmt, mrb_float(flo)); 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 -- cgit v1.2.3