mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Merge pull request #4970 from dearblue/nostdio-sprintf
Support `MRB_DISABLE_STDIO` for mruby-sprintf; ref #4954
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
|
||||
#ifdef MRB_DISABLE_STDIO
|
||||
# error sprintf conflicts 'MRB_DISABLE_STDIO' configuration in your 'build_config.rb'
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <mruby/string.h>
|
||||
@@ -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_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt)
|
||||
{
|
||||
@@ -869,7 +908,7 @@ retry:
|
||||
width--;
|
||||
}
|
||||
mrb_assert(base == 10);
|
||||
snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v);
|
||||
mrb_int2str(nbuf, sizeof(nbuf), v);
|
||||
s = nbuf;
|
||||
if (v < 0) s++; /* skip minus sign */
|
||||
}
|
||||
@@ -877,24 +916,12 @@ retry:
|
||||
s = nbuf;
|
||||
if (v < 0) {
|
||||
dots = 1;
|
||||
val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
|
||||
}
|
||||
switch (base) {
|
||||
case 2:
|
||||
if (v < 0) {
|
||||
val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
|
||||
}
|
||||
else {
|
||||
val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
|
||||
}
|
||||
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;
|
||||
else {
|
||||
val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
|
||||
}
|
||||
strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1);
|
||||
if (v < 0) {
|
||||
char d;
|
||||
|
||||
@@ -1071,7 +1098,7 @@ retry:
|
||||
need += 20;
|
||||
|
||||
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) {
|
||||
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)
|
||||
if (flags & FSPACE) *buf++ = ' ';
|
||||
|
||||
if (flags & FWIDTH) {
|
||||
n = snprintf(buf, end - buf, "%d", (int)width);
|
||||
n = mrb_int2str(buf, end - buf, width);
|
||||
buf += n;
|
||||
}
|
||||
|
||||
if (flags & FPREC) {
|
||||
n = snprintf(buf, end - buf, ".%d", (int)prec);
|
||||
*buf ++ = '.';
|
||||
n = mrb_int2str(buf, end - buf, prec);
|
||||
buf += n;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+98
-21
@@ -37,9 +37,19 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#include <mruby.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 {
|
||||
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))
|
||||
@@ -54,15 +64,44 @@ struct fmt_args {
|
||||
#define PAD_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
|
||||
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
|
||||
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 +131,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, 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
|
||||
@@ -117,11 +156,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 +208,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 +327,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 +364,33 @@ 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;
|
||||
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');
|
||||
}
|
||||
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
for (p = 0; ISDIGIT(*fmt); ++fmt) {
|
||||
@@ -353,29 +404,49 @@ 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, fl, *fmt);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
mrb_value
|
||||
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 <mruby.h>
|
||||
#include <stdio.h>
|
||||
|
||||
mrb_value
|
||||
MRB_API mrb_value
|
||||
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
|
||||
{
|
||||
char buf[25];
|
||||
@@ -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));
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user