readint.c: add new function `mrb_int_read`.

Difference from `strtoul(3)`:

* reads `mrb_int` based on configuration
* specifies the end of the string
* no sign interpretation
* base 10 only
parent 14a69c6f
......@@ -100,6 +100,7 @@ struct mrb_state;
# define MRB_ENDIAN_LOHI(a,b) b a
#endif
MRB_API mrb_int mrb_int_read(const char *p, const char *e, char **endp);
#ifndef MRB_NO_FLOAT
MRB_API double mrb_float_read(const char*, char**);
#ifdef MRB_USE_FLOAT32
......
......@@ -1234,17 +1234,16 @@ alias:
/* read suffix [0-9*_!<>] */
while (tmpl->idx < tlen) {
ch = tptr[tmpl->idx++];
ch = tptr[tmpl->idx];
if (ISDIGIT(ch)) {
count = ch - '0';
while (tmpl->idx < tlen && ISDIGIT(tptr[tmpl->idx])) {
if (count+10 > INT_MAX/10) {
mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length");
}
ch = tptr[tmpl->idx++] - '0';
count = count * 10 + ch;
char *e;
mrb_int n = mrb_int_read(tptr+tmpl->idx, tptr+tlen, &e);
if (e == NULL || n > INT_MAX) {
mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length");
}
continue; /* special case */
count = (int)n;
tmpl->idx += e - tptr;
continue;
} else if (ch == '*') {
count = -1;
} else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') {
......@@ -1258,10 +1257,11 @@ alias:
} else if (ch == '>') {
flags |= PACK_FLAG_GT;
}
} else {
tmpl->idx--;
}
else {
break;
}
tmpl->idx++;
}
if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) {
......
......@@ -245,7 +245,6 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, size_t len)
#define GETASTER(num) do { \
mrb_value tmp_v; \
t = p++; \
n = 0; \
GETNUM(n, val); \
if (*p == '$') { \
tmp_v = GETPOSARG(n); \
......@@ -260,22 +259,11 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, size_t len)
static const char *
get_num(mrb_state *mrb, const char *p, const char *end, int *valp)
{
mrb_int next_n = (int)*valp;
for (; p < end && ISDIGIT(*p); p++) {
if (mrb_int_mul_overflow(10, next_n, &next_n)) {
return NULL;
}
if (MRB_INT_MAX - (*p - '0') < next_n) {
return NULL;
}
next_n += *p - '0';
}
if (next_n > INT_MAX || next_n < 0) return NULL;
if (p >= end) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %%*[0-9]");
}
*valp = (int)next_n;
return p;
char *e;
mrb_int n = mrb_int_read(p, end, &e);
if (e == NULL || n > INT_MAX) return NULL;
*valp = (int)n;
return e;
}
static void
......@@ -665,7 +653,6 @@ retry:
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = 0;
GETNUM(n, width);
if (*p == '$') {
if (!mrb_undef_p(nextvalue)) {
......@@ -722,7 +709,6 @@ retry:
}
flags |= FPREC|FPREC0;
prec = 0;
p++;
if (*p == '*') {
GETASTER(prec);
......@@ -732,7 +718,6 @@ retry:
p++;
goto retry;
}
GETNUM(prec, precision);
goto retry;
......
#include <mruby.h>
#include <mruby/numeric.h>
#include <errno.h>
/* mrb_int_read(): read mrb_int from a string (base 10 only) */
/* const char *p - string to read */
/* const char *e - end of string */
/* char **endp - end of pased integer */
/* if integer overflows, errno will be set to ERANGE */
/* also endp will be set to NULL on overflow */
MRB_API mrb_int
mrb_int_read(const char *p, const char *e, char **endp)
{
mrb_int n = 0;
int ch;
while ((e == NULL || p < e) && ISDIGIT(*p)) {
ch = *p - '0';
if (mrb_int_mul_overflow(n, 10, &n) ||
mrb_int_add_overflow(n, ch, &n)) {
if (endp) *endp = NULL;
errno = ERANGE;
return MRB_INT_MAX;
}
p++;
}
if (endp) *endp = (char*)p;
return n;
}
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