diff -r 23cdb1f98069 -r fda81bc61037 Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Mon Mar 18 21:06:33 2013 -0700 +++ b/Lib/test/test_builtin.py Tue Mar 19 20:42:01 2013 -0700 @@ -1538,7 +1538,6 @@ class DerivedFromStr(str): pass self.assertEqual(format(0, DerivedFromStr('10')), ' 0') - @unittest.skipIf(is_jython, "FIXME #1861: bin not implemented yet.") def test_bin(self): self.assertEqual(bin(0), '0b0') self.assertEqual(bin(1), '0b1') @@ -1548,8 +1547,6 @@ self.assertEqual(bin(-(2**65)), '-0b1' + '0' * 65) self.assertEqual(bin(-(2**65-1)), '-0b' + '1' * 65) - @unittest.skipIf(is_jython, - "FIXME #1861: bytearray not implemented in Jython yet") def test_bytearray_translate(self): x = bytearray("abc") self.assertRaises(ValueError, x.translate, "1", 1) diff -r 23cdb1f98069 -r fda81bc61037 src/org/python/core/PyInteger.java --- a/src/org/python/core/PyInteger.java Mon Mar 18 21:06:33 2013 -0700 +++ b/src/org/python/core/PyInteger.java Tue Mar 19 20:42:01 2013 -0700 @@ -38,6 +38,8 @@ @Deprecated public static final BigInteger maxInt = MAX_INT; + private static final String LOOKUP = "0123456789abcdef"; + private final int value; public PyInteger(PyType subType, int v) { @@ -1090,20 +1092,48 @@ format.setGroupingUsed(true); strValue = format.format(value); } else if (value instanceof BigInteger) { - strValue = ((BigInteger) value).toString(radix); + switch (radix) { + case 2: + strValue = toBinString((BigInteger) value); + break; + case 8: + strValue = toOctString((BigInteger) value); + break; + case 16: + strValue = toHexString((BigInteger) value); + break; + default: + // TODO: emulate CPython's _PyLong_Format() for efficiency + strValue = ((BigInteger) value).toString(radix); + break; + } } else { strValue = Integer.toString((Integer) value, radix); } if (spec.alternate) { - if (radix == 2) { - strValue = "0b" + strValue; - } else if (radix == 8) { - strValue = "0o" + strValue; - } else if (radix == 16) { - strValue = "0x" + strValue; + String strPrefix = ""; + switch (radix) { + case 2: + strPrefix = "0b"; + break; + case 8: + strPrefix = "0o"; + break; + case 16: + strPrefix = "0x"; + break; + } + + if (strValue.startsWith("-")) { + assert (sign < 0); + if (!strPrefix.equals("")) + strValue = "-" + strPrefix + strValue.substring(1, strValue.length()); + } else { + strValue = strPrefix + strValue; } } + if (spec.type == 'X') { strValue = strValue.toUpperCase(); } @@ -1123,6 +1153,109 @@ return spec.pad(strValue, '>', 0); } + /** + * A more efficient algorithm for generating a hexadecimal representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, consequently, + * is implemented using expensive mathematical operations. + * + * @param value the value to generate a hexadecimal string from + * @return the hexadecimal representation of value, with "-" sign prepended if necessary + */ + static final String toHexString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) + return "0"; + + // we want to work in absolute numeric value (negative sign is added afterward) + byte[] input = value.abs().toByteArray(); + StringBuilder sb = new StringBuilder(input.length * 2); + + int b; + for (int i = 0; i < input.length; i++) { + b = input[i] & 0xFF; + sb.append(LOOKUP.charAt(b >> 4)); + sb.append(LOOKUP.charAt(b & 0x0F)); + } + + // before returning the char array as string, remove leading zeroes, but not the last one + String result = sb.toString().replaceFirst("^0+(?!$)", ""); + return signum < 0 ? "-" + result : result; + } + + /** + * A more efficient algorithm for generating an octal representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, consequently, + * is implemented using expensive mathematical operations. + * + * @param value the value to generate an octal string from + * @return the octal representation of value, with "-" sign prepended if necessary + */ + static final String toOctString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) + return "0"; + + byte[] input = value.abs().toByteArray(); + if (input.length < 3) + return value.toString(8); + + StringBuilder sb = new StringBuilder(input.length * 3); + + // working backwards, three bytes at a time + int threebytes; + int trip1, trip2, trip3; // respectively: most, middle, and least significant bytes in the triplet + for (int i = input.length - 1; i >= 0; i -= 3) { + trip3 = input[i] & 0xFF; + trip2 = ((i - 1) >= 0) ? (input[i - 1] & 0xFF) : 0x00; + trip1 = ((i - 2) >= 0) ? (input[i - 2] & 0xFF) : 0x00; + threebytes = trip3 | (trip2 << 8) | (trip1 << 16); + + // convert the three-byte value into an eight-character octal string + for (int j = 0; j < 8; j++) { + sb.append(LOOKUP.charAt((threebytes >> (j * 3)) & 0x000007)); + } + } + + String result = sb.reverse().toString().replaceFirst("^0+(?!%)", ""); + return signum < 0 ? "-" + result : result; + } + + /** + * A more efficient algorithm for generating a binary representation of a byte array. + * {@link BigInteger#toString(int)} is too slow because it generalizes to any radix and, consequently, + * is implemented using expensive mathematical operations. + * + * @param value the value to generate a binary string from + * @return the binary representation of value, with "-" sign prepended if necessary + */ + static final String toBinString(BigInteger value) { + int signum = value.signum(); + + // obvious shortcut + if (signum == 0) + return "0"; + + // we want to work in absolute numeric value (negative sign is added afterward) + byte[] input = value.abs().toByteArray(); + StringBuilder sb = new StringBuilder(value.bitCount()); + + int b; + for (int i = 0; i < input.length; i++) { + b = input[i] & 0xFF; + for (int bit = 7; bit >= 0; bit--) { + sb.append(((b >> bit) & 0x1) > 0 ? "1" : "0"); + } + } + + // before returning the char array as string, remove leading zeroes, but not the last one + String result = sb.toString().replaceFirst("^0+(?!$)", ""); + return signum < 0 ? "-" + result : result; + } + @Override public boolean isIndex() { return true; diff -r 23cdb1f98069 -r fda81bc61037 src/org/python/core/PyLong.java --- a/src/org/python/core/PyLong.java Mon Mar 18 21:06:33 2013 -0700 +++ b/src/org/python/core/PyLong.java Tue Mar 19 20:42:01 2013 -0700 @@ -977,7 +977,9 @@ @ExposedMethod(doc = BuiltinDocs.long___oct___doc) final PyString long___oct__() { - String s = getValue().toString(8); + //String s = getValue().toString(8); + String s = PyInteger.toOctString(getValue()); + if (s.startsWith("-")) { return new PyString("-0" + s.substring(1, s.length()) + "L"); } else if (s.startsWith("0")) { @@ -994,12 +996,18 @@ @ExposedMethod(doc = BuiltinDocs.long___hex___doc) final PyString long___hex__() { - String s = getValue().toString(16); - if (s.startsWith("-")) { + /* + Issue 2013: + getValue() returns a BigInteger object reference, and BigInteger.toString(radix) is known to + take computationally expensive time to compute for large numbers. + */ + //String s = getValue().toString(16); + String s = PyInteger.toHexString(getValue()); + + if (s.startsWith("-")) return new PyString("-0x" + s.substring(1, s.length()) + "L"); - } else { + else return new PyString("0x" + s + "L"); - } } @ExposedMethod(doc = BuiltinDocs.long___str___doc)