259 lines
6.1 KiB
C
259 lines
6.1 KiB
C
/* _mpz_set_str(mp_dest, string, base) -- Convert the \0-terminated
|
|
string STRING in base BASE to multiple precision integer in
|
|
MP_DEST. Allow white space in the string. If BASE == 0 determine
|
|
the base in the C standard way, i.e. 0xhh...h means base 16,
|
|
0oo...o means base 8, otherwise assume base 10.
|
|
|
|
Copyright (C) 1991 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU MP Library.
|
|
|
|
The GNU MP Library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
The GNU MP Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with the GNU MP Library; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
#include "gmp.h"
|
|
#include "gmp-impl.h"
|
|
#include "longlong.h"
|
|
|
|
enum char_type
|
|
{
|
|
XX = -3,
|
|
SPC = -2,
|
|
EOF = -1
|
|
};
|
|
|
|
static signed char ascii_to_num[256] =
|
|
{
|
|
EOF,XX, XX, XX, XX, XX, XX, XX, XX, SPC,SPC,XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
SPC,XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, XX, XX, XX, XX, XX, XX,
|
|
XX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
|
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, XX, XX, XX, XX, XX,
|
|
XX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
|
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
|
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX
|
|
};
|
|
|
|
int
|
|
#ifdef __STDC__
|
|
_mpz_set_str (MP_INT *x, const char *str, int base)
|
|
#else
|
|
_mpz_set_str (x, str, base)
|
|
MP_INT *x;
|
|
const char *str;
|
|
int base;
|
|
#endif
|
|
{
|
|
mp_ptr xp;
|
|
mp_size size;
|
|
mp_limb big_base;
|
|
int indigits_per_limb;
|
|
int negative = 0;
|
|
int inp_rawchar;
|
|
mp_limb inp_digit;
|
|
mp_limb res_digit;
|
|
size_t str_len;
|
|
mp_size i;
|
|
|
|
if (str[0] == '-')
|
|
{
|
|
negative = 1;
|
|
str++;
|
|
}
|
|
|
|
if (base == 0)
|
|
{
|
|
if (str[0] == '0')
|
|
{
|
|
if (str[1] == 'x' || str[1] == 'X')
|
|
base = 16;
|
|
else
|
|
base = 8;
|
|
}
|
|
else
|
|
base = 10;
|
|
}
|
|
|
|
big_base = __mp_bases[base].big_base;
|
|
indigits_per_limb = __mp_bases[base].chars_per_limb;
|
|
|
|
str_len = strlen (str);
|
|
|
|
size = str_len / indigits_per_limb + 1;
|
|
if (x->alloc < size)
|
|
_mpz_realloc (x, size);
|
|
xp = x->d;
|
|
|
|
size = 0;
|
|
|
|
if ((base & (base - 1)) == 0)
|
|
{
|
|
/* The base is a power of 2. Read the input string from
|
|
least to most significant character/digit. */
|
|
|
|
const char *s;
|
|
int next_bitpos;
|
|
int bits_per_indigit = big_base;
|
|
|
|
/* Accept and ignore 0x or 0X before hexadecimal numbers. */
|
|
if (base == 16 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
|
|
{
|
|
str += 2;
|
|
str_len -= 2;
|
|
}
|
|
|
|
res_digit = 0;
|
|
next_bitpos = 0;
|
|
|
|
for (s = str + str_len - 1; s >= str; s--)
|
|
{
|
|
inp_rawchar = *s;
|
|
inp_digit = ascii_to_num[inp_rawchar];
|
|
|
|
if (inp_digit >= base)
|
|
{
|
|
/* Was it white space? Just ignore it. */
|
|
if ((char) inp_digit == (char) SPC)
|
|
continue;
|
|
|
|
/* We found rubbish in the string. Return -1 to indicate
|
|
the error. */
|
|
return -1;
|
|
}
|
|
|
|
res_digit |= inp_digit << next_bitpos;
|
|
next_bitpos += bits_per_indigit;
|
|
if (next_bitpos >= BITS_PER_MP_LIMB)
|
|
{
|
|
xp[size] = res_digit;
|
|
size++;
|
|
next_bitpos -= BITS_PER_MP_LIMB;
|
|
res_digit = inp_digit >> (bits_per_indigit - next_bitpos);
|
|
}
|
|
}
|
|
|
|
xp[size] = res_digit;
|
|
size++;
|
|
for (i = size - 1; i >= 0; i--)
|
|
{
|
|
if (xp[i] != 0)
|
|
break;
|
|
}
|
|
size = i + 1;
|
|
}
|
|
else
|
|
{
|
|
/* General case. The base is not a power of 2. */
|
|
|
|
mp_size i;
|
|
int j;
|
|
mp_limb cy;
|
|
|
|
for (;;)
|
|
{
|
|
res_digit = 0;
|
|
for (j = 0; j < indigits_per_limb; )
|
|
{
|
|
inp_rawchar = (unsigned char) *str++;
|
|
inp_digit = ascii_to_num[inp_rawchar];
|
|
|
|
/* Negative means that the character was not a proper digit. */
|
|
if (inp_digit >= base)
|
|
{
|
|
/* Was it white space? Just ignore it. */
|
|
if ((char) inp_digit == (char) SPC)
|
|
continue;
|
|
|
|
goto end_or_error;
|
|
}
|
|
|
|
res_digit = res_digit * base + inp_digit;
|
|
|
|
/* Increment the loop counter here, since it mustn't be
|
|
incremented when we do "continue" above. */
|
|
j++;
|
|
}
|
|
|
|
cy = res_digit;
|
|
|
|
/* Insert RES_DIGIT into the result multi prec integer. */
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
mp_limb p1, p0;
|
|
umul_ppmm (p1, p0, big_base, xp[i]);
|
|
p0 += cy;
|
|
cy = p1 + (p0 < cy);
|
|
xp[i] = p0;
|
|
}
|
|
if (cy != 0)
|
|
{
|
|
xp[size] = cy;
|
|
size++;
|
|
}
|
|
}
|
|
|
|
end_or_error:
|
|
/* We probably have some digits in RES_DIGIT (J tells how many). */
|
|
if ((char) inp_digit != (char) EOF)
|
|
{
|
|
/* Error return. */
|
|
return -1;
|
|
}
|
|
|
|
/* J contains number of digits (in base BASE) remaining in
|
|
RES_DIGIT. */
|
|
if (j > 0)
|
|
{
|
|
big_base = 1;
|
|
do
|
|
{
|
|
big_base *= base;
|
|
j--;
|
|
}
|
|
while (j > 0);
|
|
|
|
cy = res_digit;
|
|
|
|
/* Insert ultimate RES_DIGIT into the result multi prec integer. */
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
mp_limb p1, p0;
|
|
umul_ppmm (p1, p0, big_base, xp[i]);
|
|
p0 += cy;
|
|
cy = p1 + (p0 < cy);
|
|
xp[i] = p0;
|
|
}
|
|
if (cy != 0)
|
|
{
|
|
xp[size] = cy;
|
|
size++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (negative)
|
|
size = -size;
|
|
x->size = size;
|
|
|
|
return 0;
|
|
}
|