@@ -16,6 +16,7 @@ import ./[
1616export bit_length, signbit
1717import ../../ stringobject/ strformat
1818import ../../../ Modules / unicodedata/ [decimal, space]
19+ from ../../../ Utils / utils import unreachable
1920import ../../../ Include / internal/ pycore_int
2021export PY_INT_MAX_STR_DIGITS_THRESHOLD , PY_INT_DEFAULT_MAX_STR_DIGITS
2122
@@ -776,6 +777,67 @@ proc PyLong_FromString*[C: char](s: openArray[C]; nParsed: var int; base: int =
776777 result = res.fromStr (s, nParsed, base)
777778 if result .isNil: result = res
778779
780+ proc long_format_binary (a: PyIntObject , base: uint8 , alternate: bool , v: var string ): PyBaseErrorObject =
781+ assert base in {2 u8 , 8 , 16 }
782+
783+ let
784+ size_a = a.digitCount
785+ high_a = size_a - 1
786+ let bits = case base
787+ of 16 : 4
788+ of 8 : 3
789+ of 2 : 1
790+ else : unreachable
791+ let negative = a.negative
792+ var sz: int
793+ if size_a == 0 :
794+ v = " 0"
795+ return
796+ else :
797+ # Ensure overflow doesn't occur during computation of sz.
798+ if size_a > int .high - 3 div PyLong_SHIFT:
799+ return newOverflowError newPyAscii " int too large to format"
800+ {.push overflowChecks : off .}
801+ let size_a_in_bits = (high_a) * PyLong_SHIFT + bit_length (a.digits[high_a])
802+ # Allow 1 character for a '-' sign.
803+ sz = negative.int + (size_a_in_bits + (bits - 1 )) div bits
804+ {.pop .}
805+ if alternate: sz += 2
806+ v = (when declared (newStringUninit): newStringUninit else : newString)(sz)
807+
808+ template WRITE_DIGITS (p) =
809+ # JRH: special case for power-of-2 bases
810+ var accum = TwoDigits 0
811+ var accumbits = 0 # # of bits in accum
812+ for i in 0 ..< size_a:
813+ accum = accum or ((TwoDigits a.digits[i]) shl accumbits)
814+ accumbits += PyLong_SHIFT
815+ assert accumbits >= bits
816+ while true :
817+ var cdigit = cast [uint8 ](accum and (base - 1 ))
818+ cdigit += (if cdigit < 10 : uint8 '0' else : 87 #[ uint8('a')-10]# )
819+ *-- cast [char ](cdigit)
820+ accumbits -= bits
821+ accum = accum shr bits
822+ if not (
823+ if i < high_a: accumbits >= bits
824+ else : accum > 0 ): break
825+ if alternate:
826+ case bits
827+ of 4 : *-- 'x'
828+ of 3 : *-- 'o'
829+ else : *-- 'b' # base == 2
830+ *-- '0'
831+ if negative: *-- '-'
832+
833+ var p = sz
834+ template `*--` (c) =
835+ p.dec
836+ v[p] = c
837+
838+ WRITE_DIGITS p
839+ assert p == 0
840+
779841proc fill (result: var string , i: PyIntObject ) =
780842 if i.zero:
781843 result = " 0"
@@ -816,6 +878,12 @@ proc toStringCheckThreshold*(a: PyIntObject, v: var string): PyBaseErrorObject{.
816878 if strlen > PY_INT_MAX_STR_DIGITS_THRESHOLD :
817879 check_max_str_digits strlen - int (negative) > max_str_digits
818880
881+ proc format * (i: PyIntObject , base: uint8 , s: var string ): PyBaseErrorObject =
882+ # `_PyLong_Format`
883+ # `s` is a `out` param
884+ if base == 10 : toStringCheckThreshold (i, s)
885+ else : long_format_binary (i, base, true , s)
886+
819887proc hash * (self: PyIntObject ): Hash {. inline , cdecl .} =
820888 result = hash (self.sign)
821889 for digit in self.digits:
0 commit comments