mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
9d32d440eb
The GitHub Super Linter is a more robust and better supported tool than the current GitHub Actions we are using. Running these checks: ERROR_ON_MISSING_EXEC_BIT: true VALIDATE_BASH: true VALIDATE_BASH_EXEC: true VALIDATE_EDITORCONFIG: true VALIDATE_MARKDOWN: true VALIDATE_SHELL_SHFMT: true VALIDATE_YAML: true https://github.com/marketplace/actions/super-linter https://github.com/github/super-linter Added the GitHub Super Linter badge to the README. Also updated the pre-commit framework and added more documentation on pre-commit. Added one more pre-commit check: check-executables-have-shebangs Added one extra check for merge conflicts to our GitHub Actions. EditorConfig and Markdown linting. Minor grammar and spelling fixes. Update linter.yml
464 lines
11 KiB
C
464 lines
11 KiB
C
#include <mruby.h>
|
|
#if !defined(MRB_NO_FLOAT)
|
|
#if defined(MRB_NO_STDIO) || defined(_WIN32) || defined(_WIN64)
|
|
/*
|
|
|
|
Most code in this file originates from musl (src/stdio/vfprintf.c)
|
|
which, just like mruby itself, is licensed under the MIT license.
|
|
|
|
Copyright (c) 2005-2014 Rich Felker, et al.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <ctype.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;
|
|
output_func *output;
|
|
void *opaque;
|
|
};
|
|
|
|
struct mrb_cstr {
|
|
char *buf;
|
|
size_t len;
|
|
};
|
|
|
|
#define MAX(a,b) ((a)>(b) ? (a) : (b))
|
|
#define MIN(a,b) ((a)<(b) ? (a) : (b))
|
|
|
|
/* Convenient bit representation for modifier flags, which all fall
|
|
* within 31 codepoints of the space character. */
|
|
|
|
#define ALT_FORM (1U<<('#'-' '))
|
|
#define ZERO_PAD (1U<<('0'-' '))
|
|
#define LEFT_ADJ (1U<<('-'-' '))
|
|
#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)
|
|
{
|
|
f->output(f, s, l);
|
|
}
|
|
|
|
#define PAD_SIZE 256
|
|
static void
|
|
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;
|
|
l = w - l;
|
|
memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
|
|
for (; l >= PAD_SIZE; l -= PAD_SIZE)
|
|
out(f, pad, PAD_SIZE);
|
|
out(f, pad, l);
|
|
}
|
|
|
|
static const char xdigits[16] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
|
};
|
|
|
|
static char*
|
|
fmt_u(uint32_t x, char *s)
|
|
{
|
|
for (; x; x /= 10) *--s = '0' + x % 10;
|
|
return s;
|
|
}
|
|
|
|
/* Do not override this check. The floating-point printing code below
|
|
* depends on the float.h constants being right. If they are wrong, it
|
|
* may overflow the stack. */
|
|
#if LDBL_MANT_DIG == 53
|
|
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, 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
|
|
uint32_t *a, *d, *r, *z;
|
|
uint32_t i;
|
|
int e2=0, e, j;
|
|
ptrdiff_t l;
|
|
char buf[9+LDBL_MANT_DIG/4], *s;
|
|
const char *prefix="-0X+0X 0X-0x+0x 0x";
|
|
ptrdiff_t pl;
|
|
char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
|
|
|
|
pl=1;
|
|
if (signbit(y)) {
|
|
y=-y;
|
|
} else if (fl & MARK_POS) {
|
|
prefix+=3;
|
|
} else if (fl & PAD_POS) {
|
|
prefix+=6;
|
|
} else prefix++, pl=0;
|
|
|
|
if (!isfinite(y)) {
|
|
const char *ss = (t&32)?"inf":"INF";
|
|
if (y!=y) ss=(t&32)?"nan":"NAN";
|
|
pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
|
|
out(f, prefix, pl);
|
|
out(f, ss, 3);
|
|
pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
|
|
return (int)MAX(w, 3+pl);
|
|
}
|
|
|
|
y = frexp((double)y, &e2) * 2;
|
|
if (y) e2--;
|
|
|
|
if ((t|32)=='a') {
|
|
long double round = 8.0;
|
|
ptrdiff_t re;
|
|
|
|
if (t&32) prefix += 9;
|
|
pl += 2;
|
|
|
|
if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
|
|
else re=LDBL_MANT_DIG/4-1-p;
|
|
|
|
if (re) {
|
|
while (re--) round*=16;
|
|
if (*prefix=='-') {
|
|
y=-y;
|
|
y-=round;
|
|
y+=round;
|
|
y=-y;
|
|
}
|
|
else {
|
|
y+=round;
|
|
y-=round;
|
|
}
|
|
}
|
|
|
|
estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
|
|
if (estr==ebuf) *--estr='0';
|
|
*--estr = (e2<0 ? '-' : '+');
|
|
*--estr = t+('p'-'a');
|
|
|
|
s=buf;
|
|
do {
|
|
int x=(int)y;
|
|
*s++=xdigits[x]|(t&32);
|
|
y=16*(y-x);
|
|
if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
|
|
} while (y);
|
|
|
|
if (p && s-buf-2 < p)
|
|
l = (p+2) + (ebuf-estr);
|
|
else
|
|
l = (s-buf) + (ebuf-estr);
|
|
|
|
pad(f, ' ', w, pl+l, fl);
|
|
out(f, prefix, pl);
|
|
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, ' ', w, pl+l, fl^LEFT_ADJ);
|
|
return (int)MAX(w, pl+l);
|
|
}
|
|
if (p<0) p=6;
|
|
|
|
if (y) y *= 268435456.0, e2-=28;
|
|
|
|
if (e2<0) a=r=z=big;
|
|
else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
|
|
|
|
do {
|
|
*z = (uint32_t)y;
|
|
y = 1000000000*(y-*z++);
|
|
} while (y);
|
|
|
|
while (e2>0) {
|
|
uint32_t carry=0;
|
|
int sh=MIN(29,e2);
|
|
for (d=z-1; d>=a; d--) {
|
|
uint64_t x = ((uint64_t)*d<<sh)+carry;
|
|
*d = x % 1000000000;
|
|
carry = (uint32_t)(x / 1000000000);
|
|
}
|
|
if (carry) *--a = carry;
|
|
while (z>a && !z[-1]) z--;
|
|
e2-=sh;
|
|
}
|
|
while (e2<0) {
|
|
uint32_t carry=0, *b;
|
|
int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
|
|
for (d=a; d<z; d++) {
|
|
uint32_t rm = *d & ((1<<sh)-1);
|
|
*d = (*d>>sh) + carry;
|
|
carry = (1000000000>>sh) * rm;
|
|
}
|
|
if (!*a) a++;
|
|
if (carry) *z++ = carry;
|
|
/* Avoid (slow!) computation past requested precision */
|
|
b = (t|32)=='f' ? r : a;
|
|
if (z-b > need) z = b+need;
|
|
e2+=sh;
|
|
}
|
|
|
|
if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
|
|
else e=0;
|
|
|
|
/* Perform rounding: j is precision after the radix (possibly neg) */
|
|
j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
|
|
if (j < 9*(z-r-1)) {
|
|
uint32_t x;
|
|
/* We avoid C's broken division of negative numbers */
|
|
d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
|
|
j += 9*LDBL_MAX_EXP;
|
|
j %= 9;
|
|
for (i=10, j++; j<9; i*=10, j++);
|
|
x = *d % i;
|
|
/* Are there any significant digits past j? */
|
|
if (x || d+1!=z) {
|
|
long double round = 2/LDBL_EPSILON;
|
|
long double small;
|
|
if (*d/i & 1) round += 2;
|
|
if (x<i/2) small=0.5;
|
|
else if (x==i/2 && d+1==z) small=1.0;
|
|
else small=1.5;
|
|
if (pl && *prefix=='-') round*=-1, small*=-1;
|
|
*d -= x;
|
|
/* Decide whether to round by probing round+small */
|
|
if (round+small != round) {
|
|
*d = *d + i;
|
|
while (*d > 999999999) {
|
|
*d--=0;
|
|
if (d<a) *--a=0;
|
|
(*d)++;
|
|
}
|
|
for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
|
|
}
|
|
}
|
|
if (z>d+1) z=d+1;
|
|
}
|
|
for (; z>a && !z[-1]; z--);
|
|
|
|
if ((t|32)=='g') {
|
|
if (!p) p++;
|
|
if (p>e && e>=-4) {
|
|
t--;
|
|
p-=e+1;
|
|
}
|
|
else {
|
|
t-=2;
|
|
p--;
|
|
}
|
|
if (!(fl&ALT_FORM)) {
|
|
/* Count trailing zeros in last place */
|
|
if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
|
|
else j=9;
|
|
if ((t|32)=='f')
|
|
p = MIN(p,MAX(0,9*(z-r-1)-j));
|
|
else
|
|
p = MIN(p,MAX(0,9*(z-r-1)+e-j));
|
|
}
|
|
}
|
|
l = 1 + p + (p || (fl&ALT_FORM));
|
|
if ((t|32)=='f') {
|
|
if (e>0) l+=e;
|
|
}
|
|
else {
|
|
estr=fmt_u(e<0 ? -e : e, ebuf);
|
|
while(ebuf-estr<2) *--estr='0';
|
|
*--estr = (e<0 ? '-' : '+');
|
|
*--estr = t;
|
|
l += ebuf-estr;
|
|
}
|
|
|
|
pad(f, ' ', w, pl+l, fl);
|
|
out(f, prefix, pl);
|
|
pad(f, '0', w, pl+l, fl^ZERO_PAD);
|
|
|
|
if ((t|32)=='f') {
|
|
if (a>r) a=r;
|
|
for (d=a; d<=r; d++) {
|
|
char *ss = fmt_u(*d, buf+9);
|
|
if (d!=a) while (ss>buf) *--ss='0';
|
|
else if (ss==buf+9) *--ss='0';
|
|
out(f, ss, buf+9-ss);
|
|
}
|
|
if (p || (fl&ALT_FORM)) out(f, ".", 1);
|
|
for (; d<z && p>0; d++, p-=9) {
|
|
char *ss = fmt_u(*d, buf+9);
|
|
while (ss>buf) *--ss='0';
|
|
out(f, ss, MIN(9,p));
|
|
}
|
|
pad(f, '0', p+9, 9, 0);
|
|
}
|
|
else {
|
|
if (z<=a) z=a+1;
|
|
for (d=a; d<z && p>=0; d++) {
|
|
char *ss = fmt_u(*d, buf+9);
|
|
if (ss==buf+9) *--ss='0';
|
|
if (d!=a) while (ss>buf) *--ss='0';
|
|
else {
|
|
out(f, ss++, 1);
|
|
if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
|
|
}
|
|
out(f, ss, MIN(buf+9-ss, p));
|
|
p -= (int)(buf+9-ss);
|
|
}
|
|
pad(f, '0', p+18, 18, 0);
|
|
out(f, estr, ebuf-estr);
|
|
}
|
|
|
|
pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
|
|
|
|
return (int)MAX(w, pl+l);
|
|
}
|
|
|
|
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');
|
|
}
|
|
|
|
if (*fmt == '.') {
|
|
++fmt;
|
|
for (p = 0; ISDIGIT(*fmt); ++fmt) {
|
|
p = 10 * p + (*fmt - '0');
|
|
}
|
|
}
|
|
else {
|
|
p = -1;
|
|
}
|
|
|
|
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, fl, *fmt);
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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.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 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 (int)(cstr.buf - buf);
|
|
}
|
|
#else /* MRB_NO_STDIO || _WIN32 || _WIN64 */
|
|
#include <stdio.h>
|
|
|
|
MRB_API mrb_value
|
|
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
|
|
{
|
|
char buf[25];
|
|
|
|
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_NO_STDIO || _WIN32 || _WIN64 */
|
|
#endif
|