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
MODIFY
include/mruby/value.h
include/mruby/value.h
@@ -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

MODIFY
mrbgems/mruby-pack/src/pack.c
mrbgems/mruby-pack/src/pack.c
@@ -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) {
+ 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");
}
- ch = tptr[tmpl->idx++] - '0';
- count = count * 10 + ch;
- }
- 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)) {

MODIFY
mrbgems/mruby-sprintf/src/sprintf.c
mrbgems/mruby-sprintf/src/sprintf.c
@@ -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;

ADD
src/readint.c
+#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;
+}

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