Commit 12c73615 authored by Michael T. Jones's avatar Michael T. Jones Committed by Robert Griesemer

big: refine printf formatting and optimize string conversion

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
parent 301d8a6d
......@@ -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
......
......@@ -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)
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment