From: Jens Gustedt <Jens.Gustedt@inria.fr>
To: musl@lists.openwall.com
Subject: [musl] [C23 128 bit 3/4] C23: implement w128 and wf128 for scanf and similar
Date: Wed, 31 May 2023 16:15:49 +0200 [thread overview]
Message-ID: <dd59ed3a123143ef91cf4a459a79cf5713ad0d15.1685536608.git.Jens.Gustedt@inria.fr> (raw)
In-Reply-To: <cover.1685536608.git.Jens.Gustedt@inria.fr>
These functions are possibly a bit slower with 128 bit support
enabled, since the core function `__intscan` uses wider data formats
and instructions in some places. There was already an optimization in
place for all formats which we now extend:
- first with `unsigned`, then
- switch to `uint64_t` if the value extends `UINT_MAX`, then
- switch to `uwide128` (an internal 128 bit structure) if that is unavoidable.
If the scan is erroneous because there are more digits than expected,
the error might be noticed a bit later than previously, but the
`errno` number should be the same.
---
src/internal/intscan.c | 105 ++++++++++++++++++++++++++++++++---------
src/internal/intscan.h | 4 +-
src/stdio/vfscanf.c | 25 ++++++----
src/stdio/vfwscanf.c | 19 +++++---
src/stdlib/strtol.c | 12 ++---
src/stdlib/wcstol.c | 12 ++---
6 files changed, 125 insertions(+), 52 deletions(-)
diff --git a/src/internal/intscan.c b/src/internal/intscan.c
index 57424554..c7c1e47e 100644
--- a/src/internal/intscan.c
+++ b/src/internal/intscan.c
@@ -1,6 +1,7 @@
#include <limits.h>
#include <errno.h>
#include <ctype.h>
+#include "uwide128.h"
#include "shgetc.h"
/* Lookup table for digit values. -1==255>=36 -> invalid */
@@ -23,15 +24,59 @@ static const unsigned char table[] = { -1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
};
-unsigned long long __intscan(FILE *f, unsigned base, int pok, unsigned long long lim)
+static uwide128 const uwide128_max = { { -1, -1, } };
+static uwide128 const uwide128_zero = { { 0, 0, } };
+
+static const uwide128 __uwide128_limit[37] = {
+ comp2(0, 0),
+ comp2(-1, -1),
+ comp2(-1, 9223372036854775807),
+ comp2(6148914691236517205, 6148914691236517205),
+ comp2(-1, 4611686018427387903),
+ comp2(3689348814741910323, 3689348814741910323),
+ comp2(-6148914691236517206, 3074457345618258602),
+ comp2(5270498306774157604, 2635249153387078802),
+ comp2(-1, 2305843009213693951),
+ comp2(-4099276460824344804, 2049638230412172401),
+ comp2(-7378697629483820647, 1844674407370955161),
+ comp2(8384883669867978007, 1676976733973595601),
+ comp2(6148914691236517205, 1537228672809129301),
+ comp2(4256940940086819603, 1418980313362273201),
+ comp2(2635249153387078802, 1317624576693539401),
+ comp2(1229782938247303441, 1229782938247303441),
+ comp2(-1, 1152921504606846975),
+ comp2(1085102592571150095, 1085102592571150095),
+ comp2(-2049638230412172402, 1024819115206086200),
+ comp2(-1941762534074689644, 970881267037344821),
+ comp2(-3689348814741910324, 922337203685477580),
+ comp2(-4392081922311798004, 878416384462359600),
+ comp2(-5030930201920786805, 838488366986797800),
+ comp2(4812194106185100421, 802032351030850070),
+ comp2(-6148914691236517206, 768614336404564650),
+ comp2(-6640827866535438582, 737869762948382064),
+ comp2(-7094901566811366007, 709490156681136600),
+ comp2(-1366425486941448268, 683212743470724133),
+ comp2(-7905747460161236407, 658812288346769700),
+ comp2(-3180473116156819245, 636094623231363848),
+ comp2(-8608480567731124088, 614891469123651720),
+ comp2(-8925843906633654008, 595056260442243600),
+ comp2(-1, 576460752303423487),
+ comp2(8943875914525843207, 558992244657865200),
+ comp2(-8680820740569200761, 542551296285575047),
+ comp2(8432797290838652167, 527049830677415760),
+ comp2(8198552921648689607, 512409557603043100),
+};
+
+uwide128 __intscan(FILE *f, unsigned base, int pok, uwide128 lim)
{
const unsigned char *val = table+1;
int c, neg=0;
unsigned x;
- unsigned long long y;
+ uint64_t z;
+ uwide128 y;
if (base > 36 || base == 1) {
errno = EINVAL;
- return 0;
+ return uwide128_zero;
}
while (isspace((c=shgetc(f))));
if (c=='+' || c=='-') {
@@ -46,7 +91,7 @@ unsigned long long __intscan(FILE *f, unsigned base, int pok, unsigned long long
shunget(f);
if (pok) shunget(f);
else shlim(f, 0);
- return 0;
+ return uwide128_zero;
}
base = 16;
} else if ((c|32)=='b' && base != 16) {
@@ -55,7 +100,7 @@ unsigned long long __intscan(FILE *f, unsigned base, int pok, unsigned long long
shunget(f);
if (pok) shunget(f);
else shlim(f, 0);
- return 0;
+ return uwide128_zero;
}
base = 2;
} else if (base == 0) {
@@ -67,43 +112,59 @@ unsigned long long __intscan(FILE *f, unsigned base, int pok, unsigned long long
shunget(f);
shlim(f, 0);
errno = EINVAL;
- return 0;
+ return uwide128_zero;
}
}
if (base == 10) {
for (x=0; c-'0'<10U && x<=UINT_MAX/10-1; c=shgetc(f))
x = x*10 + (c-'0');
- for (y=x; c-'0'<10U && y<=ULLONG_MAX/10 && 10*y<=ULLONG_MAX-(c-'0'); c=shgetc(f))
- y = y*10 + (c-'0');
+ for (z=x; c-'0'<10U && z<=UINT64_MAX/10-1; c=shgetc(f))
+ z = z*10 + (c-'0');
+ y=__uwide128_u64(x);
+ if (c-'0'<10U) {
+ uwide128 y10 = __uwide128_mul(y, 10);
+ while (c-'0'<10U
+ && __uwide128_le(y, __uwide128_limit[10])
+ && __uwide128_le(y10, __uwide128_sub(uwide128_max, (c-'0')))) {
+ y = __uwide128_add(y10, (c-'0'));
+ y10 = __uwide128_mul(y, 10);
+ c=shgetc(f);
+ }
+ }
if (c-'0'>=10U) goto done;
- } else if (!(base & base-1)) {
- int bs = "\0\1\2\4\7\3\6\5"[(0x17*base)>>5&7];
- for (x=0; val[c]<base && x<=UINT_MAX/32; c=shgetc(f))
- x = x<<bs | val[c];
- for (y=x; val[c]<base && y<=ULLONG_MAX>>bs; c=shgetc(f))
- y = y<<bs | val[c];
} else {
for (x=0; val[c]<base && x<=UINT_MAX/36-1; c=shgetc(f))
x = x*base + val[c];
- for (y=x; val[c]<base && y<=ULLONG_MAX/base && base*y<=ULLONG_MAX-val[c]; c=shgetc(f))
- y = y*base + val[c];
+ for (z=x; val[c]<base && z<=UINT64_MAX/36-1; c=shgetc(f))
+ z = z*base + val[c];
+ y=__uwide128_u64(z);
+ if (val[c]<base) {
+ uwide128 yBase = __uwide128_mul(y, base);
+ while (val[c]<base
+ && __uwide128_le(y, __uwide128_limit[base])
+ && __uwide128_le(yBase, __uwide128_sub(uwide128_max, val[c]))) {
+ y = __uwide128_add(yBase, val[c]);
+ yBase = __uwide128_mul(y, base);
+ c=shgetc(f);
+ }
+ }
}
if (val[c]<base) {
for (; val[c]<base; c=shgetc(f));
errno = ERANGE;
y = lim;
- if (lim&1) neg = 0;
+ if (lim.v64[word64(0)]&1) neg = 0;
}
done:
shunget(f);
- if (y>=lim) {
- if (!(lim&1) && !neg) {
+ if (__uwide128_le(lim, y)) {
+ if (!(lim.v64[word64(0)]&1) && !neg) {
errno = ERANGE;
- return lim-1;
- } else if (y>lim) {
+ return __uwide128_sub(lim, 1);
+ } else if (!__uwide128_le(y, lim)) {
errno = ERANGE;
return lim;
}
}
- return (y^neg)-neg;
+ return neg ? __uwide128_neg(y) : y;
}
diff --git a/src/internal/intscan.h b/src/internal/intscan.h
index ccf9f112..cf7a9d8f 100644
--- a/src/internal/intscan.h
+++ b/src/internal/intscan.h
@@ -1,8 +1,10 @@
#ifndef INTSCAN_H
#define INTSCAN_H
+#include "stdio_impl.h"
#include <stdio.h>
+#include "uwide128.h"
-hidden unsigned long long __intscan(FILE *, unsigned, int, unsigned long long);
+hidden uwide128 __intscan(FILE *, unsigned, int, uwide128);
#endif
diff --git a/src/stdio/vfscanf.c b/src/stdio/vfscanf.c
index f8ace92f..be922f82 100644
--- a/src/stdio/vfscanf.c
+++ b/src/stdio/vfscanf.c
@@ -18,25 +18,29 @@
#define SIZE_l 1
#define SIZE_L 2
#define SIZE_ll 3
+#define SIZE_128 4
-static void store_int(void *dest, int size, unsigned long long i)
+static void store_int(void *dest, int size, uwide128 i)
{
if (!dest) return;
switch (size) {
case SIZE_hh:
- *(char *)dest = i;
+ *(char *)dest = i.v64[lo64];
break;
case SIZE_h:
- *(short *)dest = i;
+ *(short *)dest = i.v64[lo64];
break;
case SIZE_def:
- *(int *)dest = i;
+ *(int *)dest = i.v64[lo64];
break;
case SIZE_l:
- *(long *)dest = i;
+ *(long *)dest = i.v64[lo64];
break;
case SIZE_ll:
- *(long long *)dest = i;
+ *(long long *)dest = i.v64[lo64];
+ break;
+ case SIZE_128:
+ *(uwide128 *)dest = i;
break;
}
}
@@ -77,7 +81,7 @@ int vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap)
void *dest=NULL;
int invert;
int matches=0;
- unsigned long long x;
+ uwide128 x;
long double y;
off_t pos = 0;
unsigned char scanset[257];
@@ -178,6 +182,7 @@ int vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap)
#else
case 64: size = SIZE_ll; break;
#endif
+ case 128: size = SIZE_128; break;
}
break;
case 'b': case 'd': case 'i': case 'o': case 'u': case 'x':
@@ -206,7 +211,7 @@ int vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap)
case '[':
break;
case 'n':
- store_int(dest, size, pos);
+ store_int(dest, size, __uwide128_u64(pos));
/* do not increment match count, etc! */
continue;
default:
@@ -326,9 +331,9 @@ int vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap)
case 'i':
base = 0;
int_common:
- x = __intscan(f, base, 0, ULLONG_MAX);
+ x = __intscan(f, base, 0, UWIDE128_MAX);
if (!shcnt(f)) goto match_fail;
- if (t=='p' && dest) *(void **)dest = (void *)(uintptr_t)x;
+ if (t=='p' && dest) *(void **)dest = (void *)(uintptr_t)x.v64[lo64];
else store_int(dest, size, x);
break;
case 'a': case 'A':
diff --git a/src/stdio/vfwscanf.c b/src/stdio/vfwscanf.c
index 6fe2749c..f70d83a1 100644
--- a/src/stdio/vfwscanf.c
+++ b/src/stdio/vfwscanf.c
@@ -18,25 +18,29 @@
#define SIZE_l 1
#define SIZE_L 2
#define SIZE_ll 3
+#define SIZE_128 4
-static void store_int(void *dest, int size, unsigned long long i)
+static void store_int(void *dest, int size, uwide128 i)
{
if (!dest) return;
switch (size) {
case SIZE_hh:
- *(char *)dest = i;
+ *(char *)dest = i.v64[lo64];
break;
case SIZE_h:
- *(short *)dest = i;
+ *(short *)dest = i.v64[lo64];
break;
case SIZE_def:
- *(int *)dest = i;
+ *(int *)dest = i.v64[lo64];
break;
case SIZE_l:
- *(long *)dest = i;
+ *(long *)dest = i.v64[lo64];
break;
case SIZE_ll:
- *(long long *)dest = i;
+ *(long long *)dest = i.v64[lo64];
+ break;
+ case SIZE_128:
+ *(uwide128 *)dest = i;
break;
}
}
@@ -201,6 +205,7 @@ int vfwscanf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
#else
case 64: size = SIZE_ll; break;
#endif
+ case 128: size = SIZE_128; break;
}
break;
case 'b': case 'd': case 'i': case 'o': case 'u': case 'x':
@@ -234,7 +239,7 @@ int vfwscanf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
switch (t) {
case 'n':
- store_int(dest, size, pos);
+ store_int(dest, size, __uwide128_i64(pos));
/* do not increment match count, etc! */
continue;
diff --git a/src/stdlib/strtol.c b/src/stdlib/strtol.c
index bfefea69..db5acadb 100644
--- a/src/stdlib/strtol.c
+++ b/src/stdlib/strtol.c
@@ -5,12 +5,12 @@
#include <limits.h>
#include <ctype.h>
-static unsigned long long strtox(const char *s, char **p, int base, unsigned long long lim)
+static unsigned long long strtox(const char *s, char **p, int base, uwide128 lim)
{
FILE f;
sh_fromstring(&f, s);
shlim(&f, 0);
- unsigned long long y = __intscan(&f, base, 1, lim);
+ unsigned long long y = __intscan(&f, base, 1, lim).v64[lo64];
if (p) {
size_t cnt = shcnt(&f);
*p = (char *)s + cnt;
@@ -20,22 +20,22 @@ static unsigned long long strtox(const char *s, char **p, int base, unsigned lon
unsigned long long strtoull(const char *restrict s, char **restrict p, int base)
{
- return strtox(s, p, base, ULLONG_MAX);
+ return strtox(s, p, base, __uwide128_u64(ULLONG_MAX));
}
long long strtoll(const char *restrict s, char **restrict p, int base)
{
- return strtox(s, p, base, LLONG_MIN);
+ return strtox(s, p, base, __uwide128_u64(0ULL+LLONG_MIN));
}
unsigned long strtoul(const char *restrict s, char **restrict p, int base)
{
- return strtox(s, p, base, ULONG_MAX);
+ return strtox(s, p, base, __uwide128_u64(ULONG_MAX));
}
long strtol(const char *restrict s, char **restrict p, int base)
{
- return strtox(s, p, base, 0UL+LONG_MIN);
+ return strtox(s, p, base, __uwide128_u64(0UL+LONG_MIN));
}
intmax_t strtoimax(const char *restrict s, char **restrict p, int base)
diff --git a/src/stdlib/wcstol.c b/src/stdlib/wcstol.c
index 1eeb495f..d7662562 100644
--- a/src/stdlib/wcstol.c
+++ b/src/stdlib/wcstol.c
@@ -29,7 +29,7 @@ static size_t do_read(FILE *f, unsigned char *buf, size_t len)
return 0;
}
-static unsigned long long wcstox(const wchar_t *s, wchar_t **p, int base, unsigned long long lim)
+static unsigned long long wcstox(const wchar_t *s, wchar_t **p, int base, uwide128 lim)
{
wchar_t *t = (wchar_t *)s;
unsigned char buf[64];
@@ -42,7 +42,7 @@ static unsigned long long wcstox(const wchar_t *s, wchar_t **p, int base, unsign
while (iswspace(*t)) t++;
f.cookie = (void *)t;
shlim(&f, 0);
- unsigned long long y = __intscan(&f, base, 1, lim);
+ unsigned long long y = __intscan(&f, base, 1, lim).v64[lo64];
if (p) {
size_t cnt = shcnt(&f);
*p = cnt ? t + cnt : (wchar_t *)s;
@@ -52,22 +52,22 @@ static unsigned long long wcstox(const wchar_t *s, wchar_t **p, int base, unsign
unsigned long long wcstoull(const wchar_t *restrict s, wchar_t **restrict p, int base)
{
- return wcstox(s, p, base, ULLONG_MAX);
+ return wcstox(s, p, base, __uwide128_u64(ULLONG_MAX));
}
long long wcstoll(const wchar_t *restrict s, wchar_t **restrict p, int base)
{
- return wcstox(s, p, base, LLONG_MIN);
+ return wcstox(s, p, base, __uwide128_u64(0ULL+LLONG_MIN));
}
unsigned long wcstoul(const wchar_t *restrict s, wchar_t **restrict p, int base)
{
- return wcstox(s, p, base, ULONG_MAX);
+ return wcstox(s, p, base, __uwide128_u64(ULONG_MAX));
}
long wcstol(const wchar_t *restrict s, wchar_t **restrict p, int base)
{
- return wcstox(s, p, base, 0UL+LONG_MIN);
+ return wcstox(s, p, base, __uwide128_u64(0UL+LONG_MIN));
}
intmax_t wcstoimax(const wchar_t *restrict s, wchar_t **restrict p, int base)
--
2.34.1
next prev parent reply other threads:[~2023-05-31 14:16 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-05-31 14:15 [musl] [C23 128 bit 0/4] implement the library part of 128 bit support Jens Gustedt
2023-05-31 14:15 ` [musl] [C23 128 bit 1/4] add an emulation for 128 bit arithmetic as needed for C library support Jens Gustedt
2023-05-31 14:15 ` [musl] [C23 128 bit 2/4] C23: implement w128 and wf128 support for printf Jens Gustedt
2023-05-31 14:15 ` Jens Gustedt [this message]
2023-05-31 14:15 ` [musl] [C23 128 bit 4/4] C23: implement proper support for int128_t and uint128_t Jens Gustedt
2023-05-31 14:27 ` Rich Felker
2023-05-31 14:29 ` Rich Felker
2023-05-31 14:36 ` Jₑₙₛ Gustedt
2023-05-31 14:41 ` Rich Felker
2023-05-31 14:55 ` Jₑₙₛ Gustedt
2023-05-31 14:57 ` Rich Felker
2023-05-31 15:07 ` Jₑₙₛ Gustedt
2023-05-31 15:14 ` Rich Felker
2023-05-31 15:37 ` Jₑₙₛ Gustedt
2023-05-31 15:40 ` Rich Felker
2023-05-31 15:56 ` Jₑₙₛ Gustedt
2023-05-31 16:30 ` Alexander Monakov
2023-05-31 16:58 ` Jens Gustedt
2023-05-31 17:03 ` Rich Felker
2023-05-31 17:09 ` Alexander Monakov
2023-06-01 7:24 ` Jₑₙₛ Gustedt
2023-05-31 14:42 ` Jₑₙₛ Gustedt
2023-05-31 14:47 ` Rich Felker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=dd59ed3a123143ef91cf4a459a79cf5713ad0d15.1685536608.git.Jens.Gustedt@inria.fr \
--to=jens.gustedt@inria.fr \
--cc=musl@lists.openwall.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).