msvcrt: Support arbitrary buffer size in bnum.

Signed-off-by: Piotr Caban <piotr@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Piotr Caban 2020-07-23 15:38:25 +02:00 committed by Alexandre Julliard
parent 413e34df58
commit 7495821974
3 changed files with 112 additions and 100 deletions

View file

@ -26,19 +26,26 @@ static const int p10s[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
#define LIMB_DIGITS 9 /* each DWORD stores up to 9 digits */
#define LIMB_MAX 1000000000 /* 10^9 */
#define BNUM_IDX(i) ((i) & 127)
#define BNUM_PREC64 128 /* data size needed to store 64-bit double */
/* bnum represents real number with fixed decimal point (after 2 limbs) */
struct bnum {
DWORD data[128]; /* circular buffer, base 10 number */
int b; /* least significant digit position */
int e; /* most significant digit position + 1 */
int size; /* data buffer size in DWORDS (power of 2) */
DWORD data[1]; /* circular buffer, base 10 number */
};
static inline int bnum_idx(struct bnum *b, int idx)
{
return idx & (b->size - 1);
}
/* Returns integral part of bnum */
static inline ULONGLONG bnum_to_mant(struct bnum *b)
{
ULONGLONG ret = (ULONGLONG)b->data[BNUM_IDX(b->e-1)] * LIMB_MAX;
if(b->b != b->e-1) ret += b->data[BNUM_IDX(b->e-2)];
ULONGLONG ret = (ULONGLONG)b->data[bnum_idx(b, b->e-1)] * LIMB_MAX;
if(b->b != b->e-1) ret += b->data[bnum_idx(b, b->e-2)];
return ret;
}
@ -53,20 +60,20 @@ static inline BOOL bnum_lshift(struct bnum *b, int shift)
assert(shift <= 29);
for(i=b->b; i<b->e; i++) {
tmp = ((ULONGLONG)b->data[BNUM_IDX(i)] << shift) + rest;
tmp = ((ULONGLONG)b->data[bnum_idx(b, i)] << shift) + rest;
rest = tmp / LIMB_MAX;
b->data[BNUM_IDX(i)] = tmp % LIMB_MAX;
b->data[bnum_idx(b, i)] = tmp % LIMB_MAX;
if(i == b->b && !b->data[BNUM_IDX(i)])
if(i == b->b && !b->data[bnum_idx(b, i)])
b->b++;
}
if(rest) {
b->data[BNUM_IDX(b->e)] = rest;
b->data[bnum_idx(b, b->e)] = rest;
b->e++;
if(BNUM_IDX(b->b) == BNUM_IDX(b->e)) {
if(b->data[BNUM_IDX(b->b)]) b->data[BNUM_IDX(b->b+1)] |= 1;
if(bnum_idx(b, b->b) == bnum_idx(b, b->e)) {
if(b->data[bnum_idx(b, b->b)]) b->data[bnum_idx(b, b->b+1)] |= 1;
b->b++;
}
return TRUE;
@ -85,21 +92,21 @@ static inline BOOL bnum_rshift(struct bnum *b, int shift)
assert(shift <= 9);
for(i=b->e-1; i>=b->b; i--) {
tmp = b->data[BNUM_IDX(i)] & ((1<<shift)-1);
b->data[BNUM_IDX(i)] = (b->data[BNUM_IDX(i)] >> shift) + rest;
tmp = b->data[bnum_idx(b, i)] & ((1<<shift)-1);
b->data[bnum_idx(b, i)] = (b->data[bnum_idx(b, i)] >> shift) + rest;
rest = (LIMB_MAX >> shift) * tmp;
if(i==b->e-1 && !b->data[BNUM_IDX(i)]) {
if(i==b->e-1 && !b->data[bnum_idx(b, i)]) {
b->e--;
ret = TRUE;
}
}
if(rest) {
if(BNUM_IDX(b->b-1) == BNUM_IDX(b->e)) {
if(rest) b->data[BNUM_IDX(b->b)] |= 1;
if(bnum_idx(b, b->b-1) == bnum_idx(b, b->e)) {
if(rest) b->data[bnum_idx(b, b->b)] |= 1;
} else {
b->b--;
b->data[BNUM_IDX(b->b)] = rest;
b->data[bnum_idx(b, b->b)] = rest;
}
}
return ret;
@ -114,20 +121,20 @@ static inline void bnum_mult(struct bnum *b, int mult)
assert(mult <= LIMB_MAX);
for(i=b->b; i<b->e; i++) {
tmp = ((ULONGLONG)b->data[BNUM_IDX(i)] * mult) + rest;
tmp = ((ULONGLONG)b->data[bnum_idx(b, i)] * mult) + rest;
rest = tmp / LIMB_MAX;
b->data[BNUM_IDX(i)] = tmp % LIMB_MAX;
b->data[bnum_idx(b, i)] = tmp % LIMB_MAX;
if(i == b->b && !b->data[BNUM_IDX(i)])
if(i == b->b && !b->data[bnum_idx(b, i)])
b->b++;
}
if(rest) {
b->data[BNUM_IDX(b->e)] = rest;
b->data[bnum_idx(b, b->e)] = rest;
b->e++;
if(BNUM_IDX(b->b) == BNUM_IDX(b->e)) {
if(b->data[BNUM_IDX(b->b)]) b->data[BNUM_IDX(b->b+1)] |= 1;
if(bnum_idx(b, b->b) == bnum_idx(b, b->e)) {
if(b->data[bnum_idx(b, b->b)]) b->data[bnum_idx(b, b->b+1)] |= 1;
b->b++;
}
}

View file

@ -577,11 +577,12 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
double v, pf_flags *flags, MSVCRT__locale_t locale, BOOL three_digit_exp)
{
int e2, e10 = 0, round_pos, round_limb, radix_pos, first_limb_len, i, len, r, ret;
BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC64])];
struct bnum *b = (struct bnum*)bnum_data;
APICHAR buf[LIMB_DIGITS + 1];
BOOL trim_tail = FALSE;
pf_flags f;
int limb_len, prec;
struct bnum b;
ULONGLONG m;
DWORD l;
@ -594,33 +595,35 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
if(v) {
m = (ULONGLONG)1 << (MANT_BITS - 1);
m |= (*(ULONGLONG*)&v & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1));
b.data[0] = m % LIMB_MAX;
b.data[1] = m / LIMB_MAX;
b.b = 0;
b.e = 2;
b->b = 0;
b->e = 2;
b->size = BNUM_PREC64;
b->data[0] = m % LIMB_MAX;
b->data[1] = m / LIMB_MAX;
e2 -= MANT_BITS;
while(e2 > 0) {
int shift = e2 > 29 ? 29 : e2;
if(bnum_lshift(&b, shift)) e10 += LIMB_DIGITS;
if(bnum_lshift(b, shift)) e10 += LIMB_DIGITS;
e2 -= shift;
}
while(e2 < 0) {
int shift = -e2 > 9 ? 9 : -e2;
if(bnum_rshift(&b, shift)) e10 -= LIMB_DIGITS;
if(bnum_rshift(b, shift)) e10 -= LIMB_DIGITS;
e2 += shift;
}
} else {
b.b = 0;
b.e = 1;
b.data[0] = 0;
b->b = 0;
b->e = 1;
b->size = BNUM_PREC64;
b->data[0] = 0;
e10 = -LIMB_DIGITS;
}
if(!b.data[BNUM_IDX(b.e-1)])
if(!b->data[bnum_idx(b, b->e-1)])
first_limb_len = 1;
else
first_limb_len = floor(log10(b.data[BNUM_IDX(b.e - 1)])) + 1;
first_limb_len = floor(log10(b->data[bnum_idx(b, b->e - 1)])) + 1;
radix_pos = first_limb_len + LIMB_DIGITS + e10;
round_pos = flags->Precision;
@ -629,11 +632,11 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
else if(!flags->Precision || flags->Format=='e' || flags->Format=='E')
round_pos++;
if (round_pos <= first_limb_len)
round_limb = b.e + (first_limb_len - round_pos) / LIMB_DIGITS - 1;
round_limb = b->e + (first_limb_len - round_pos) / LIMB_DIGITS - 1;
else
round_limb = b.e - (round_pos - first_limb_len - 1) / LIMB_DIGITS - 2;
round_limb = b->e - (round_pos - first_limb_len - 1) / LIMB_DIGITS - 2;
if (b.b<=round_limb && round_limb<b.e) {
if (b->b<=round_limb && round_limb<b->e) {
BOOL round_up = FALSE;
if (round_pos <= first_limb_len) {
@ -644,43 +647,43 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
}
if (round_pos) {
l = b.data[BNUM_IDX(round_limb)] % p10s[round_pos];
b.data[BNUM_IDX(round_limb)] -= l;
l = b->data[bnum_idx(b, round_limb)] % p10s[round_pos];
b->data[bnum_idx(b, round_limb)] -= l;
if(2*l >= p10s[round_pos]) round_up = TRUE;
} else if(round_limb - 1 >= b.b) {
if(2*b.data[BNUM_IDX(round_limb-1)] >= LIMB_MAX) round_up = TRUE;
} else if(round_limb - 1 >= b->b) {
if(2*b->data[bnum_idx(b, round_limb-1)] >= LIMB_MAX) round_up = TRUE;
}
b.b = round_limb;
b->b = round_limb;
if(round_up) {
b.data[BNUM_IDX(b.b)] += p10s[round_pos];
for(i = b.b; i < b.e; i++) {
if(b.data[BNUM_IDX(i)] < LIMB_MAX) break;
b->data[bnum_idx(b, b->b)] += p10s[round_pos];
for(i = b->b; i < b->e; i++) {
if(b->data[bnum_idx(b, i)] < LIMB_MAX) break;
b.data[BNUM_IDX(i)] -= LIMB_MAX;
if(i+1 < b.e) b.data[BNUM_IDX(i+1)]++;
else b.data[BNUM_IDX(i+1)] = 1;
b->data[bnum_idx(b, i)] -= LIMB_MAX;
if(i+1 < b->e) b->data[bnum_idx(b, i+1)]++;
else b->data[bnum_idx(b, i+1)] = 1;
}
if(i == b.e-1) {
if(!b.data[BNUM_IDX(b.e-1)])
if(i == b->e-1) {
if(!b->data[bnum_idx(b, b->e-1)])
i = 1;
else
i = floor(log10(b.data[BNUM_IDX(b.e-1)])) + 1;
i = floor(log10(b->data[bnum_idx(b, b->e-1)])) + 1;
if(i != first_limb_len) {
first_limb_len = i;
radix_pos++;
}
} else if(i == b.e) {
} else if(i == b->e) {
first_limb_len = 1;
radix_pos++;
b.e++;
b->e++;
}
}
}
else if(b.e <= round_limb) { /* got 0 or 1 after rounding */
b.data[BNUM_IDX(round_limb)] = b.e==round_limb && b.data[BNUM_IDX(b.e-1)]>=LIMB_MAX/2;
b.b = round_limb;
b.e = b.b + 1;
else if(b->e <= round_limb) { /* got 0 or 1 after rounding */
b->data[bnum_idx(b, round_limb)] = b->e==round_limb && b->data[bnum_idx(b, b->e-1)]>=LIMB_MAX/2;
b->b = round_limb;
b->e = b->b + 1;
first_limb_len = 1;
radix_pos++;
}
@ -702,9 +705,9 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
}
if(trim_tail && !flags->Alternate) {
for(i=round_limb; flags->Precision>0 && i<b.e; i++) {
if(i>=b.b)
l = b.data[BNUM_IDX(i)];
for(i=round_limb; flags->Precision>0 && i<b->e; i++) {
if(i>=b->b)
l = b->data[bnum_idx(b, i)];
else
l = 0;
@ -713,7 +716,7 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
r = radix_pos + flags->Precision;
else
r = flags->Precision + 1;
r = first_limb_len + LIMB_DIGITS * (b.e-1 - b.b) - r;
r = first_limb_len + LIMB_DIGITS * (b->e-1 - b->b) - r;
r %= LIMB_DIGITS;
if(r < 0) r += LIMB_DIGITS;
l /= p10s[r];
@ -770,9 +773,9 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
}
limb_len = LIMB_DIGITS;
for(i=b.e-1; radix_pos>0 && i>=b.b; i--) {
limb_len = (i == b.e-1 ? first_limb_len : LIMB_DIGITS);
l = b.data[BNUM_IDX(i)];
for(i=b->e-1; radix_pos>0 && i>=b->b; i--) {
limb_len = (i == b->e-1 ? first_limb_len : LIMB_DIGITS);
l = b->data[bnum_idx(b, i)];
if(limb_len > radix_pos) {
f.Precision = radix_pos;
l /= p10s[limb_len - radix_pos];
@ -811,8 +814,8 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
ret += r;
}
for(; prec>0 && i>=b.b; i--) {
l = b.data[BNUM_IDX(i)];
for(; prec>0 && i>=b->b; i--) {
l = b->data[bnum_idx(b, i)];
if(limb_len != LIMB_DIGITS)
l %= p10s[limb_len];
if(limb_len > prec) {
@ -837,7 +840,7 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
ret += r;
}
} else {
l = b.data[BNUM_IDX(b.e - 1)];
l = b->data[bnum_idx(b, b->e - 1)];
l /= p10s[first_limb_len - 1];
buf[0] = '0' + l;
@ -854,9 +857,9 @@ static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *pu
prec = flags->Precision;
limb_len = LIMB_DIGITS;
for(i=b.e-1; prec>0 && i>=b.b; i--) {
l = b.data[BNUM_IDX(i)];
if(i == b.e-1) {
for(i=b->e-1; prec>0 && i>=b->b; i--) {
l = b->data[bnum_idx(b, i)];
if(i == b->e-1) {
limb_len = first_limb_len - 1;
l %= p10s[limb_len];
}

View file

@ -581,10 +581,11 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
int matched=0;
#endif
BOOL found_digit = FALSE, found_dp = FALSE, found_sign = FALSE;
BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC64])];
int e2 = 0, dp=0, sign=1, off, limb_digits = 0, i;
struct bnum *b = (struct bnum*)bnum_data;
enum round round = ROUND_ZERO;
MSVCRT_wchar_t nch;
struct bnum b;
nch = get(ctx);
if(nch == '-') {
@ -637,27 +638,28 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
nch = get(ctx);
}
b.data[0] = 0;
b.b = 0;
b.e = 1;
b->b = 0;
b->e = 1;
b->size = BNUM_PREC64;
b->data[0] = 0;
while(nch>='0' && nch<='9') {
found_digit = TRUE;
if(limb_digits == LIMB_DIGITS) {
if(BNUM_IDX(b.b-1) == BNUM_IDX(b.e)) break;
if(bnum_idx(b, b->b-1) == bnum_idx(b, b->e)) break;
else {
b.b--;
b.data[BNUM_IDX(b.b)] = 0;
b->b--;
b->data[bnum_idx(b, b->b)] = 0;
limb_digits = 0;
}
}
b.data[BNUM_IDX(b.b)] = b.data[BNUM_IDX(b.b)] * 10 + nch - '0';
b->data[bnum_idx(b, b->b)] = b->data[bnum_idx(b, b->b)] * 10 + nch - '0';
limb_digits++;
nch = get(ctx);
dp++;
}
while(nch>='0' && nch<='9') {
if(nch != '0') b.data[BNUM_IDX(b.b)] |= 1;
if(nch != '0') b->data[bnum_idx(b, b->b)] |= 1;
nch = get(ctx);
dp++;
}
@ -668,7 +670,7 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
}
/* skip leading '0' */
if(nch=='0' && !limb_digits && !b.b) {
if(nch=='0' && !limb_digits && !b->b) {
found_digit = TRUE;
while(nch == '0') {
nch = get(ctx);
@ -679,20 +681,20 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
while(nch>='0' && nch<='9') {
found_digit = TRUE;
if(limb_digits == LIMB_DIGITS) {
if(BNUM_IDX(b.b-1) == BNUM_IDX(b.e)) break;
if(bnum_idx(b, b->b-1) == bnum_idx(b, b->e)) break;
else {
b.b--;
b.data[BNUM_IDX(b.b)] = 0;
b->b--;
b->data[bnum_idx(b, b->b)] = 0;
limb_digits = 0;
}
}
b.data[BNUM_IDX(b.b)] = b.data[BNUM_IDX(b.b)] * 10 + nch - '0';
b->data[bnum_idx(b, b->b)] = b->data[bnum_idx(b, b->b)] * 10 + nch - '0';
limb_digits++;
nch = get(ctx);
}
while(nch>='0' && nch<='9') {
if(nch != '0') b.data[BNUM_IDX(b.b)] |= 1;
if(nch != '0') b->data[bnum_idx(b, b->b)] |= 1;
nch = get(ctx);
}
@ -746,23 +748,23 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
unget(ctx);
}
if(!b.data[BNUM_IDX(b.e-1)]) return make_double(sign, 0, 0, ROUND_ZERO, err);
if(!b->data[bnum_idx(b, b->e-1)]) return make_double(sign, 0, 0, ROUND_ZERO, err);
/* Fill last limb with 0 if needed */
if(b.b+1 != b.e) {
if(b->b+1 != b->e) {
for(; limb_digits != LIMB_DIGITS; limb_digits++)
b.data[BNUM_IDX(b.b)] *= 10;
b->data[bnum_idx(b, b->b)] *= 10;
}
for(; BNUM_IDX(b.b) < BNUM_IDX(b.e); b.b++) {
if(b.data[BNUM_IDX(b.b)]) break;
for(; bnum_idx(b, b->b) < bnum_idx(b, b->e); b->b++) {
if(b->data[bnum_idx(b, b->b)]) break;
}
/* move decimal point to limb boundary */
if(limb_digits==dp && b.b==b.e-1)
return make_double(sign, 0, b.data[BNUM_IDX(b.e-1)], ROUND_ZERO, err);
if(limb_digits==dp && b->b==b->e-1)
return make_double(sign, 0, b->data[bnum_idx(b, b->e-1)], ROUND_ZERO, err);
off = (dp - limb_digits) % LIMB_DIGITS;
if(off < 0) off += LIMB_DIGITS;
if(off) bnum_mult(&b, p10s[off]);
if(off) bnum_mult(b, p10s[off]);
if(dp-1 > MSVCRT_DBL_MAX_10_EXP)
return make_double(sign, INT_MAX, 1, ROUND_ZERO, err);
@ -772,27 +774,27 @@ double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
return make_double(sign, INT_MIN, 1, ROUND_ZERO, err);
while(dp > 2*LIMB_DIGITS) {
if(bnum_rshift(&b, 9)) dp -= LIMB_DIGITS;
if(bnum_rshift(b, 9)) dp -= LIMB_DIGITS;
e2 += 9;
}
while(dp <= LIMB_DIGITS) {
if(bnum_lshift(&b, 29)) dp += LIMB_DIGITS;
if(bnum_lshift(b, 29)) dp += LIMB_DIGITS;
e2 -= 29;
}
while(b.data[BNUM_IDX(b.e-1)] < LIMB_MAX/10) {
bnum_lshift(&b, 1);
while(b->data[bnum_idx(b, b->e-1)] < LIMB_MAX/10) {
bnum_lshift(b, 1);
e2--;
}
/* Check if fractional part is non-zero */
/* Caution: it's only correct because bnum_to_mant returns more than 53 bits */
for(i=b.e-3; i>=b.b; i--) {
if (!b.data[BNUM_IDX(b.b)]) continue;
for(i=b->e-3; i>=b->b; i--) {
if (!b->data[bnum_idx(b, b->b)]) continue;
round = ROUND_DOWN;
break;
}
return make_double(sign, e2, bnum_to_mant(&b), round, err);
return make_double(sign, e2, bnum_to_mant(b), round, err);
}
static MSVCRT_wchar_t strtod_str_get(void *ctx)