@@ -185,16 +185,15 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter
185185 /*
186186 Fast path for radixes that are a power of two.
187187 */
188+ count := count_bits (a) or_return
189+
188190 if is_power_of_two (int (radix)) {
189191 if zero_terminate {
190192 available -= 1
191193 buffer[available] = 0
192194 }
193195
194- shift, count: int
195- // mask := _WORD(radix - 1);
196- shift, err = log (DIGIT (radix), 2 )
197- count, err = count_bits (a)
196+ shift := log (DIGIT (radix), 2 ) or_return
198197 digit: _WORD
199198
200199 for offset := 0 ; offset < count; offset += shift {
@@ -224,7 +223,17 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter
224223 return written, nil
225224 }
226225
227- return _itoa_raw_full (a, radix, buffer, zero_terminate)
226+ // NOTE(Jeroen): The new method is faster for an `Int` up to ~32768 bits in size with optimizations.
227+ // At `.None` or `.Minimal`, it appears to always be faster.
228+ // If we optimize `itoa` further, this needs to be evaluated.
229+ itoa_method := _itoa_raw_full
230+
231+ when ODIN_OPTIMIZATION_MODE >= .Size {
232+ if count >= 32768 {
233+ itoa_method = _itoa_raw_old
234+ }
235+ }
236+ return itoa_method (a, radix, buffer, zero_terminate)
228237}
229238
230239itoa :: proc {int_itoa_string, int_itoa_raw}
@@ -601,14 +610,89 @@ RADIX_TABLE_REVERSE_SIZE :: 80
601610 Stores a bignum as a ASCII string in a given radix (2..64)
602611 The buffer must be appropriately sized. This routine doesn't check.
603612*/
613+
604614_itoa_raw_full :: proc (a: ^Int, radix: i8 , buffer: []u8 , zero_terminate := false , allocator := context .allocator) -> (written: int , err: Error) {
605615 assert_if_nil (a)
606616 context .allocator = allocator
607617
608- temp, denominator := &Int{}, &Int{}
618+ // Calculate largest radix^n that fits within _DIGIT_BITS
619+ divisor := ITOA_DIVISOR
620+ digit_count := ITOA_COUNT
621+ _radix := DIGIT (radix)
622+
623+ if radix != 10 {
624+ i := _WORD (1 )
625+ digit_count = -1
626+ for i < _WORD (1 << _DIGIT_BITS) {
627+ divisor = DIGIT (i)
628+ i *= _WORD (radix)
629+ digit_count += 1
630+ }
631+ }
609632
610- internal_copy (temp, a) or_return
611- internal_set (denominator, radix) or_return
633+ temp := &Int{}
634+ internal_copy (temp, a) or_return
635+ defer internal_destroy (temp)
636+
637+ available := len (buffer)
638+ if zero_terminate {
639+ available -= 1
640+ buffer[available] = 0
641+ }
642+
643+ if a.sign == .Negative {
644+ temp.sign = .Zero_or_Positive
645+ }
646+
647+ remainder: DIGIT
648+ for {
649+ if remainder, err = internal_divmod (temp, temp, divisor); err != nil {
650+ return len (buffer) - available, err
651+ }
652+
653+ count := digit_count
654+ for available > 0 && count > 0 {
655+ available -= 1
656+ buffer[available] = RADIX_TABLE[remainder % _radix]
657+ remainder /= _radix
658+ count -= 1
659+ }
660+
661+ if temp.used == 0 {
662+ break
663+ }
664+ }
665+
666+ // Remove leading zero if we ended up with one.
667+ if buffer[available] == ' 0' {
668+ available += 1
669+ }
670+
671+ if a.sign == .Negative {
672+ available -= 1
673+ buffer[available] = ' -'
674+ }
675+
676+ /*
677+ If we overestimated the size, we need to move the buffer left.
678+ */
679+ written = len (buffer) - available
680+ if written < len (buffer) {
681+ diff := len (buffer) - written
682+ mem.copy (&buffer[0 ], &buffer[diff], written)
683+ }
684+ return written, nil
685+ }
686+
687+ // Old internal digit extraction procedure.
688+ // We're keeping this around as ground truth for the tests.
689+ _itoa_raw_old :: proc (a: ^Int, radix: i8 , buffer: []u8 , zero_terminate := false , allocator := context .allocator) -> (written: int , err: Error) {
690+ assert_if_nil (a)
691+ context .allocator = allocator
692+
693+ temp := &Int{}
694+ internal_copy (temp, a) or_return
695+ defer internal_destroy (temp)
612696
613697 available := len (buffer)
614698 if zero_terminate {
@@ -623,7 +707,6 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false
623707 remainder: DIGIT
624708 for {
625709 if remainder, err = #force_inline internal_divmod (temp, temp, DIGIT (radix)); err != nil {
626- internal_destroy (temp, denominator)
627710 return len (buffer) - available, err
628711 }
629712 available -= 1
@@ -638,8 +721,6 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false
638721 buffer[available] = ' -'
639722 }
640723
641- internal_destroy (temp, denominator)
642-
643724 /*
644725 If we overestimated the size, we need to move the buffer left.
645726 */
0 commit comments