mirror of
https://github.com/golang/go
synced 2024-11-22 07:34:40 -07:00
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
This commit is contained in:
parent
301d8a6d4a
commit
12c736158a
@ -316,9 +316,26 @@ func charset(ch int) string {
|
|||||||
return "" // unknown format
|
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
|
// Format is a support routine for fmt.Formatter. It accepts
|
||||||
// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
|
// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
|
||||||
// (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
|
// (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) {
|
func (x *Int) Format(s fmt.State, ch int) {
|
||||||
cs := charset(ch)
|
cs := charset(ch)
|
||||||
@ -334,72 +351,69 @@ func (x *Int) Format(s fmt.State, ch int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine format
|
// determine sign character
|
||||||
format := "%s"
|
sign := ""
|
||||||
if s.Flag('#') {
|
|
||||||
switch ch {
|
|
||||||
case 'o':
|
|
||||||
format = "0%s"
|
|
||||||
case 'x':
|
|
||||||
format = "0x%s"
|
|
||||||
case 'X':
|
|
||||||
format = "0X%s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 {
|
switch {
|
||||||
case x.neg:
|
case x.neg:
|
||||||
prefix = "-"
|
sign = "-"
|
||||||
case s.Flag('+'):
|
case s.Flag('+'): // supersedes ' ' when both specified
|
||||||
prefix = "+"
|
sign = "+"
|
||||||
case s.Flag(' ') && ch != 'x' && ch != 'X':
|
case s.Flag(' '):
|
||||||
prefix = " "
|
sign = " "
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill to minimum width and prepend sign prefix
|
// determine prefix characters for indicating output base
|
||||||
if width, ok := s.Width(); ok && len(t)+len(prefix) < width {
|
prefix := ""
|
||||||
if s.Flag('0') {
|
if s.Flag('#') {
|
||||||
t = fmt.Sprintf("%s%0*d%s", prefix, width-len(t)-len(prefix), 0, t)
|
switch ch {
|
||||||
} else {
|
case 'o': // octal
|
||||||
if s.Flag('-') {
|
prefix = "0"
|
||||||
width = -width
|
case 'x': // hexadecimal
|
||||||
|
prefix = "0x"
|
||||||
|
case 'X':
|
||||||
|
prefix = "0X"
|
||||||
}
|
}
|
||||||
t = fmt.Sprintf("%*s", width, prefix+t)
|
|
||||||
}
|
|
||||||
} else if prefix != "" {
|
|
||||||
t = prefix + t
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprint(s, t)
|
// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// scan sets z to the integer value corresponding to the longest possible prefix
|
||||||
|
@ -366,12 +366,10 @@ var formatTests = []struct {
|
|||||||
{"1234", "%-5d", "1234 "},
|
{"1234", "%-5d", "1234 "},
|
||||||
{"1234", "%x", "4d2"},
|
{"1234", "%x", "4d2"},
|
||||||
{"1234", "%X", "4D2"},
|
{"1234", "%X", "4D2"},
|
||||||
{"1234", "% x", "4 d2"},
|
|
||||||
{"-1234", "%3x", "-4d2"},
|
{"-1234", "%3x", "-4d2"},
|
||||||
{"-1234", "%4x", "-4d2"},
|
{"-1234", "%4x", "-4d2"},
|
||||||
{"-1234", "%5x", " -4d2"},
|
{"-1234", "%5x", " -4d2"},
|
||||||
{"-1234", "%-5x", "-4d2 "},
|
{"-1234", "%-5x", "-4d2 "},
|
||||||
{"-1234", "% x", "-4 d2"},
|
|
||||||
{"1234", "%03d", "1234"},
|
{"1234", "%03d", "1234"},
|
||||||
{"1234", "%04d", "1234"},
|
{"1234", "%04d", "1234"},
|
||||||
{"1234", "%05d", "01234"},
|
{"1234", "%05d", "01234"},
|
||||||
@ -380,11 +378,103 @@ var formatTests = []struct {
|
|||||||
{"1234", "%+06d", "+01234"},
|
{"1234", "%+06d", "+01234"},
|
||||||
{"1234", "% 06d", " 01234"},
|
{"1234", "% 06d", " 01234"},
|
||||||
{"1234", "%-6d", "1234 "},
|
{"1234", "%-6d", "1234 "},
|
||||||
{"1234", "%-06d", "001234"},
|
{"1234", "%-06d", "1234 "},
|
||||||
{"-1234", "%-06d", "-01234"},
|
{"-1234", "%-06d", "-1234 "},
|
||||||
{"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // 10**100
|
|
||||||
"% x",
|
{"1234", "%.3d", "1234"},
|
||||||
"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", "%.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) {
|
func TestFormat(t *testing.T) {
|
||||||
@ -399,7 +489,7 @@ func TestFormat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
output := fmt.Sprintf(test.format, x)
|
output := fmt.Sprintf(test.format, x)
|
||||||
if output != test.output {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user