summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2003-02-02 07:51:32 (GMT)
committerTim Peters <tim.peters@gmail.com>2003-02-02 07:51:32 (GMT)
commitbf2674be0e95787cdeb154091b7377e30b2827bf (patch)
tree05ccb3b212ff3027587b948678959f7b6ca61546 /Objects
parent06dd8cf5e4100303714f9e3f72b3c52330fdd51c (diff)
downloadcpython-bf2674be0e95787cdeb154091b7377e30b2827bf.zip
cpython-bf2674be0e95787cdeb154091b7377e30b2827bf.tar.gz
cpython-bf2674be0e95787cdeb154091b7377e30b2827bf.tar.bz2
long(string, base) now takes time linear in len(string) when base is a
power of 2. Enabled the tail end of test_long() in pickletester.py because it no longer takes forever when run from test_pickle.py.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/longobject.c123
1 files changed, 108 insertions, 15 deletions
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 9d7243f..7ca8244 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -1090,6 +1090,95 @@ long_format(PyObject *aa, int base, int addL)
return (PyObject *)str;
}
+/* *str points to the first digit in a string of base base digits. base
+ * is a power of 2 (2, 4, 8, 16, or 32). *str is set to point to the first
+ * non-digit (which may be *str!). A normalized long is returned.
+ * The point to this routine is that it takes time linear in the number of
+ * string characters.
+ */
+static PyLongObject *
+long_from_binary_base(char **str, int base)
+{
+ char *p = *str;
+ char *start = p;
+ int bits_per_char;
+ int n;
+ PyLongObject *z;
+ twodigits accum;
+ int bits_in_accum;
+ digit *pdigit;
+
+ assert(base >= 2 && base <= 32 && (base & (base - 1)) == 0);
+ n = base;
+ for (bits_per_char = -1; n; ++bits_per_char)
+ n >>= 1;
+ /* n <- total # of bits needed, while setting p to end-of-string */
+ n = 0;
+ for (;;) {
+ int k = -1;
+ char ch = *p;
+
+ if (ch <= '9')
+ k = ch - '0';
+ else if (ch >= 'a')
+ k = ch - 'a' + 10;
+ else if (ch >= 'A')
+ k = ch - 'A' + 10;
+ if (k < 0 || k >= base)
+ break;
+ n += bits_per_char;
+ if (n < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "long string too large to convert");
+ return NULL;
+ }
+ ++p;
+ }
+ *str = p;
+ /* n <- # of Python digits needed, = ceiling(n/SHIFT). */
+ n = (n + SHIFT - 1) / SHIFT;
+ z = _PyLong_New(n);
+ if (z == NULL)
+ return NULL;
+ /* Read string from right, and fill in long from left; i.e.,
+ * from least to most significant in both.
+ */
+ accum = 0;
+ bits_in_accum = 0;
+ pdigit = z->ob_digit;
+ while (--p >= start) {
+ unsigned char ch = (unsigned char)*p;
+ digit k;
+
+ if (ch <= '9')
+ k = ch - '0';
+ else if (ch >= 'a')
+ k = ch - 'a' + 10;
+ else {
+ assert(ch >= 'A');
+ k = ch - 'A' + 10;
+ }
+ assert(k >= 0 && k <= base);
+ accum |= k << bits_in_accum;
+ bits_in_accum += bits_per_char;
+ if (bits_in_accum >= SHIFT) {
+ *pdigit++ = (digit)(accum & MASK);
+ assert(pdigit - z->ob_digit <= n);
+ accum >>= SHIFT;
+ bits_in_accum -= SHIFT;
+ assert(bits_in_accum < SHIFT);
+ }
+ }
+ if (bits_in_accum) {
+ assert(bits_in_accum <= SHIFT);
+ *pdigit++ = (digit)accum;
+ assert(pdigit - z->ob_digit <= n);
+ }
+ while (pdigit - z->ob_digit < n)
+ *pdigit++ = 0;
+ return long_normalize(z);
+}
+
PyObject *
PyLong_FromString(char *str, char **pend, int base)
{
@@ -1122,23 +1211,27 @@ PyLong_FromString(char *str, char **pend, int base)
}
if (base == 16 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
str += 2;
- z = _PyLong_New(0);
start = str;
- for ( ; z != NULL; ++str) {
- int k = -1;
- PyLongObject *temp;
+ if ((base & (base - 1)) == 0)
+ z = long_from_binary_base(&str, base);
+ else {
+ z = _PyLong_New(0);
+ for ( ; z != NULL; ++str) {
+ int k = -1;
+ PyLongObject *temp;
- if (*str <= '9')
- k = *str - '0';
- else if (*str >= 'a')
- k = *str - 'a' + 10;
- else if (*str >= 'A')
- k = *str - 'A' + 10;
- if (k < 0 || k >= base)
- break;
- temp = muladd1(z, (digit)base, (digit)k);
- Py_DECREF(z);
- z = temp;
+ if (*str <= '9')
+ k = *str - '0';
+ else if (*str >= 'a')
+ k = *str - 'a' + 10;
+ else if (*str >= 'A')
+ k = *str - 'A' + 10;
+ if (k < 0 || k >= base)
+ break;
+ temp = muladd1(z, (digit)base, (digit)k);
+ Py_DECREF(z);
+ z = temp;
+ }
}
if (z == NULL)
return NULL;