[Python-checkins] r54212 - sandbox/trunk/pep3101/unicodeformat.c

eric.smith python-checkins at python.org
Wed Mar 7 23:07:13 CET 2007


Author: eric.smith
Date: Wed Mar  7 23:07:08 2007
New Revision: 54212

Modified:
   sandbox/trunk/pep3101/unicodeformat.c
Log:
Removed longintrepr.h.  I no longer look at PyLong internals; instead everything works through functions exported by PyLong, although some begin with underscores like _PyLong_Sign().  The only code this affected is the base 2 output for longs in _format_long_binary().

Modified: sandbox/trunk/pep3101/unicodeformat.c
==============================================================================
--- sandbox/trunk/pep3101/unicodeformat.c	(original)
+++ sandbox/trunk/pep3101/unicodeformat.c	Wed Mar  7 23:07:08 2007
@@ -20,9 +20,6 @@
 #define C_UNICODE 1
 #endif
 
-/* we need access to a PyLongObject's internals */
-#include "longintrepr.h"
-
 #if C_UNICODE
 #define CH_TYPE                  Py_UNICODE
 #define CH_TYPE_ISDECIMAL        Py_UNICODE_ISDECIMAL
@@ -1115,21 +1112,33 @@
     return p_digits;
 }
 
+/* call _PyLong_AsByteArray to get the bytes, then do the formatting.
+   note that for negative numbers, the byte array will be two's
+   complement, and we need to undo that. */
 static int
-_format_long_binary(PyObject *v, FmtState *fs, const InternalFormatSpec *format)
-{
-    /* we know that v is a PyLongObject */
+_format_long_binary(PyObject *v, FmtState *fs, const
+InternalFormatSpec *format) { /* we know that v is a PyLongObject */
     PyLongObject* l = (PyLongObject*)v;
 
     IntegerFieldWidths spec;
     CH_TYPE *pbuf;
     CH_TYPE *start;
-    char sign = _PyLong_Sign(v) >= 0 ? '\0' : '-';
+
+    size_t bytes_required;
+    int is_negative = _PyLong_Sign(v) < 0;
+    unsigned char* byte_buffer;
+    unsigned char* start_byte_buffer;
+    char sign = is_negative ? '-' : '\0';
     Py_ssize_t n_digits = _PyLong_NumBits(v);
-    Py_ssize_t i;
+    int is_zero = n_digits == 0;
+    int carry_bit;
 
-    /* special case for zero */
-    if (l->ob_size == 0)
+    /* check for error getting number of bits */
+    if (n_digits < 0)
+        return 0;
+
+    /* special case for zero.  it takes one digit, despite having zero bits */
+    if (is_zero)
         n_digits = 1;
 
     _calc_integer_widths(&spec, sign, n_digits, format);
@@ -1143,24 +1152,71 @@
                           format->fill_char == '\0' ? ' ' : format->fill_char);
 
     /* degenerate case for zero.  handle it and get out */
-    if (l->ob_size == 0) {
+    if (is_zero) {
         *pbuf = '0';
         return 1;
     }
 
-    /* finally, fill in the digits, starting at the right and working left */
+    /* copy the bytes into the output buffer.  we know they'll fit,
+       because we have enough space for a textual representation */
+
+    /* how many bytes this long will require.  we allocate on extra
+       bit (by not subtracting 1 from the number of bits) because the
+       sign bit might be needed */
+    bytes_required = n_digits / 8 + 1;
+    byte_buffer = (unsigned char*)start;  /* where to put the bytes */
+    start_byte_buffer = byte_buffer;
+    /* use little endian so we can start at the lsb and overwrite as we
+       go higher */
+    if (_PyLong_AsByteArray(l, byte_buffer, bytes_required, 0, 1) < 0)
+        return 0;
+
+    /* adjust byte_buffer to point to the end of the string of digits */
+    byte_buffer += bytes_required - 1;
+    /* and adjust the output pointer to point to the end of the
+       CH_TYPE output buffer */
     pbuf = start + n_digits - 1;
 
-    for (i = 0; i < ABS(l->ob_size); i++) {
-        Py_ssize_t j;
-        digit d = l->ob_digit[i];
-        for (j = 0; j < SHIFT; j++, d >>= 1) {
-            if (d & 1)
+    carry_bit = 1;  /* two's complement setup */
+
+    /* finally, fill in the digits, starting at the right and working
+       left */
+    while (1) {
+        int j;
+        unsigned char byte = *byte_buffer;
+
+        /* this test is just trying to make sure we don't go past the
+           beginning of the byte buffer.  IIRC, it's illegal to
+           decrement a pointer past the beginning of what it points
+           to.  not that it probably matters, but let's be as
+           conformant as possible */
+        if (byte_buffer != start_byte_buffer)
+            byte_buffer--;
+
+        /* two's complement algorithm */
+        if (is_negative) {
+            byte = ~byte;
+            if (byte == 0xff && carry_bit) {
+                /* this is the only case where the carry bit will be
+                   set */
+                byte = 0;
+                carry_bit = 1;
+            } else {
+                /* we know adding won't overflow */
+                byte += carry_bit;
+                carry_bit = 0;
+            }
+        }
+
+        /* loop over each bit.  this could be sped up with a table
+           lookup, but the mid-byte termination makes it complex */
+        for (j = 0; j < 8; j++, byte >>= 1) {
+            if (byte & 1)
                 *pbuf = '1';
             else
                 *pbuf = '0';
 
-            /* see if we're done mid-digit */
+            /* see if we're done mid-byte */
             if (pbuf == start)
                 goto DONE;
             pbuf--;


More information about the Python-checkins mailing list