]> Cypherpunks repositories - gostls13.git/commitdiff
big: refine printf formatting and optimize string conversion
authorMichael T. Jones <mtj@google.com>
Thu, 21 Jul 2011 21:29:08 +0000 (14:29 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 21 Jul 2011 21:29:08 +0000 (14:29 -0700)
Now handles standard precision specifications, standard interactions of
redundant specifications (such as precision and zero-fill), handles the
special case of precision specified but equal to zero, and generates the
output without recursive calls to format/printf to be clearer and faster.

R=gri, mtj, gri
CC=golang-dev
https://golang.org/cl/4703050

src/pkg/big/int.go
src/pkg/big/int_test.go

index 0aad189ad0205e8106132ad289f79993faa6e8dd..a9a70203311cadcfab293c225644d075dab8f8c8 100755 (executable)
@@ -316,9 +316,26 @@ func charset(ch int) string {
        return "" // unknown format
 }
 
+// write count copies of text to s
+func writeMultiple(s fmt.State, text string, count int) {
+       if len(text) > 0 {
+               b := []byte(text)
+               for ; count > 0; count-- {
+                       s.Write(b)
+               }
+       }
+}
+
 // Format is a support routine for fmt.Formatter. It accepts
 // the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
 // (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
+// Also supported are the full suite of "Printf" style format
+// codes for integral types, including PLUS, MINUS, and SPACE
+// for sign control, HASH for leading ZERO in octal and for
+// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X"
+// respectively, specification of minimum digits precision,
+// output field width, space or zero padding, and left or
+// right justification.
 //
 func (x *Int) Format(s fmt.State, ch int) {
        cs := charset(ch)
@@ -334,72 +351,69 @@ func (x *Int) Format(s fmt.State, ch int) {
                return
        }
 
-       // determine format
-       format := "%s"
+       // determine sign character
+       sign := ""
+       switch {
+       case x.neg:
+               sign = "-"
+       case s.Flag('+'): // supersedes ' ' when both specified
+               sign = "+"
+       case s.Flag(' '):
+               sign = " "
+       }
+
+       // determine prefix characters for indicating output base
+       prefix := ""
        if s.Flag('#') {
                switch ch {
-               case 'o':
-                       format = "0%s"
-               case 'x':
-                       format = "0x%s"
+               case 'o': // octal
+                       prefix = "0"
+               case 'x': // hexadecimal
+                       prefix = "0x"
                case 'X':
-                       format = "0X%s"
+                       prefix = "0X"
                }
        }
-       t := fmt.Sprintf(format, x.abs.string(cs))
-
-       // insert spaces in hexadecimal formats if needed
-       if len(t) > 0 && s.Flag(' ') && (ch == 'x' || ch == 'X') {
-               spaces := (len(t)+1)/2 - 1
-               spaced := make([]byte, len(t)+spaces)
-               var i, j int
-               spaced[i] = t[j]
-               i++
-               j++
-               if len(t)&1 == 0 {
-                       spaced[i] = t[j]
-                       i++
-                       j++
-               }
-               for j < len(t) {
-                       spaced[i] = ' '
-                       i++
-                       spaced[i] = t[j]
-                       i++
-                       j++
-                       spaced[i] = t[j]
-                       i++
-                       j++
-               }
-               t = string(spaced)
-       }
 
-       // determine sign prefix
-       prefix := ""
-       switch {
-       case x.neg:
-               prefix = "-"
-       case s.Flag('+'):
-               prefix = "+"
-       case s.Flag(' ') && ch != 'x' && ch != 'X':
-               prefix = " "
+       // determine digits with base set by len(cs) and digit characters from cs
+       digits := x.abs.string(cs)
+
+       // number of characters for the Sprintf family's three classes of number padding
+       var left int   // space characters to left of digits for right justification ("%8d")
+       var zeroes int // zero characters (acutally cs[0]) as left-most digits ("%.8d")
+       var right int  // space characters to right of digits for left justification ("%-8d")
+
+       // determine number padding from precision: the least number of digits to output
+       precision, precisionSet := s.Precision()
+       if precisionSet {
+               switch {
+               case len(digits) < precision:
+                       zeroes = precision - len(digits) // count of zero padding 
+               case digits == "0" && precision == 0:
+                       return // print nothing if zero value (x == 0) and zero precision ("." or ".0")
+               }
        }
 
-       // fill to minimum width and prepend sign prefix
-       if width, ok := s.Width(); ok && len(t)+len(prefix) < width {
-               if s.Flag('0') {
-                       t = fmt.Sprintf("%s%0*d%s", prefix, width-len(t)-len(prefix), 0, t)
-               } else {
-                       if s.Flag('-') {
-                               width = -width
-                       }
-                       t = fmt.Sprintf("%*s", width, prefix+t)
+       // determine field pad from width: the least number of characters to output
+       length := len(sign) + len(prefix) + zeroes + len(digits)
+       if width, widthSet := s.Width(); widthSet && length < width { // pad as specified
+               switch d := width - length; {
+               case s.Flag('-'): // pad on the right with spaces. supersedes '0' when both specified
+                       right = d
+               case s.Flag('0') && !precisionSet: // pad with zeroes unless precision also specified
+                       zeroes = d
+               default: // pad on the left with spaces
+                       left = d
                }
-       } else if prefix != "" {
-               t = prefix + t
        }
 
-       fmt.Fprint(s, t)
+       // print Int as [left pad][sign][prefix][zero pad][digits][right pad]
+       writeMultiple(s, " ", left)
+       writeMultiple(s, sign, 1)
+       writeMultiple(s, prefix, 1)
+       writeMultiple(s, "0", zeroes)
+       writeMultiple(s, digits, 1)
+       writeMultiple(s, " ", right)
 }
 
 // scan sets z to the integer value corresponding to the longest possible prefix
index 593d38ebb80da6a79dd4bda72b0e5b99e5f177bd..03446d6ae2d9cf81398401505dd3df0400cec4b9 100755 (executable)
@@ -366,12 +366,10 @@ var formatTests = []struct {
        {"1234", "%-5d", "1234 "},
        {"1234", "%x", "4d2"},
        {"1234", "%X", "4D2"},
-       {"1234", "% x", "4 d2"},
        {"-1234", "%3x", "-4d2"},
        {"-1234", "%4x", "-4d2"},
        {"-1234", "%5x", " -4d2"},
        {"-1234", "%-5x", "-4d2 "},
-       {"-1234", "% x", "-4 d2"},
        {"1234", "%03d", "1234"},
        {"1234", "%04d", "1234"},
        {"1234", "%05d", "01234"},
@@ -380,11 +378,103 @@ var formatTests = []struct {
        {"1234", "%+06d", "+01234"},
        {"1234", "% 06d", " 01234"},
        {"1234", "%-6d", "1234  "},
-       {"1234", "%-06d", "001234"},
-       {"-1234", "%-06d", "-01234"},
-       {"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // 10**100
-               "% x",
-               "12 49 ad 25 94 c3 7c eb 0b 27 84 c4 ce 0b f3 8a ce 40 8e 21 1a 7c aa b2 43 08 a8 2e 8f 10 00 00 00 00 00 00 00 00 00 00 00 00"},
+       {"1234", "%-06d", "1234  "},
+       {"-1234", "%-06d", "-1234 "},
+
+       {"1234", "%.3d", "1234"},
+       {"1234", "%.4d", "1234"},
+       {"1234", "%.5d", "01234"},
+       {"1234", "%.6d", "001234"},
+       {"-1234", "%.3d", "-1234"},
+       {"-1234", "%.4d", "-1234"},
+       {"-1234", "%.5d", "-01234"},
+       {"-1234", "%.6d", "-001234"},
+
+       {"1234", "%8.3d", "    1234"},
+       {"1234", "%8.4d", "    1234"},
+       {"1234", "%8.5d", "   01234"},
+       {"1234", "%8.6d", "  001234"},
+       {"-1234", "%8.3d", "   -1234"},
+       {"-1234", "%8.4d", "   -1234"},
+       {"-1234", "%8.5d", "  -01234"},
+       {"-1234", "%8.6d", " -001234"},
+
+       {"1234", "%+8.3d", "   +1234"},
+       {"1234", "%+8.4d", "   +1234"},
+       {"1234", "%+8.5d", "  +01234"},
+       {"1234", "%+8.6d", " +001234"},
+       {"-1234", "%+8.3d", "   -1234"},
+       {"-1234", "%+8.4d", "   -1234"},
+       {"-1234", "%+8.5d", "  -01234"},
+       {"-1234", "%+8.6d", " -001234"},
+
+       {"1234", "% 8.3d", "    1234"},
+       {"1234", "% 8.4d", "    1234"},
+       {"1234", "% 8.5d", "   01234"},
+       {"1234", "% 8.6d", "  001234"},
+       {"-1234", "% 8.3d", "   -1234"},
+       {"-1234", "% 8.4d", "   -1234"},
+       {"-1234", "% 8.5d", "  -01234"},
+       {"-1234", "% 8.6d", " -001234"},
+
+       {"1234", "%.3x", "4d2"},
+       {"1234", "%.4x", "04d2"},
+       {"1234", "%.5x", "004d2"},
+       {"1234", "%.6x", "0004d2"},
+       {"-1234", "%.3x", "-4d2"},
+       {"-1234", "%.4x", "-04d2"},
+       {"-1234", "%.5x", "-004d2"},
+       {"-1234", "%.6x", "-0004d2"},
+
+       {"1234", "%8.3x", "     4d2"},
+       {"1234", "%8.4x", "    04d2"},
+       {"1234", "%8.5x", "   004d2"},
+       {"1234", "%8.6x", "  0004d2"},
+       {"-1234", "%8.3x", "    -4d2"},
+       {"-1234", "%8.4x", "   -04d2"},
+       {"-1234", "%8.5x", "  -004d2"},
+       {"-1234", "%8.6x", " -0004d2"},
+
+       {"1234", "%+8.3x", "    +4d2"},
+       {"1234", "%+8.4x", "   +04d2"},
+       {"1234", "%+8.5x", "  +004d2"},
+       {"1234", "%+8.6x", " +0004d2"},
+       {"-1234", "%+8.3x", "    -4d2"},
+       {"-1234", "%+8.4x", "   -04d2"},
+       {"-1234", "%+8.5x", "  -004d2"},
+       {"-1234", "%+8.6x", " -0004d2"},
+
+       {"1234", "% 8.3x", "     4d2"},
+       {"1234", "% 8.4x", "    04d2"},
+       {"1234", "% 8.5x", "   004d2"},
+       {"1234", "% 8.6x", "  0004d2"},
+       {"1234", "% 8.7x", " 00004d2"},
+       {"1234", "% 8.8x", " 000004d2"},
+       {"-1234", "% 8.3x", "    -4d2"},
+       {"-1234", "% 8.4x", "   -04d2"},
+       {"-1234", "% 8.5x", "  -004d2"},
+       {"-1234", "% 8.6x", " -0004d2"},
+       {"-1234", "% 8.7x", "-00004d2"},
+       {"-1234", "% 8.8x", "-000004d2"},
+
+       {"1234", "%-8.3d", "1234    "},
+       {"1234", "%-8.4d", "1234    "},
+       {"1234", "%-8.5d", "01234   "},
+       {"1234", "%-8.6d", "001234  "},
+       {"1234", "%-8.7d", "0001234 "},
+       {"1234", "%-8.8d", "00001234"},
+       {"-1234", "%-8.3d", "-1234   "},
+       {"-1234", "%-8.4d", "-1234   "},
+       {"-1234", "%-8.5d", "-01234  "},
+       {"-1234", "%-8.6d", "-001234 "},
+       {"-1234", "%-8.7d", "-0001234"},
+       {"-1234", "%-8.8d", "-00001234"},
+
+       {"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1
+
+       {"0", "%.d", ""},
+       {"0", "%.0d", ""},
+       {"0", "%3.d", ""},
 }
 
 func TestFormat(t *testing.T) {
@@ -399,7 +489,7 @@ func TestFormat(t *testing.T) {
                }
                output := fmt.Sprintf(test.format, x)
                if output != test.output {
-                       t.Errorf("#%d got %q; want %q", i, output, test.output)
+                       t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output)
                }
        }
 }