Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

log: fix formatting of big.Int #22679

Merged
merged 3 commits into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 34 additions & 24 deletions log/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,20 +359,23 @@ func formatLogfmtValue(value interface{}, term bool) string {
return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
case float64:
return strconv.FormatFloat(v, floatFormat, 3, 64)
case int8, uint8:
return fmt.Sprintf("%d", value)
case int:
return FormatLogfmtInt64(int64(v))
case int8:
return strconv.FormatInt(int64(v), 10)
case uint8:
return strconv.FormatInt(int64(v), 10)
case int16:
return strconv.FormatInt(int64(v), 10)
case uint16:
return strconv.FormatInt(int64(v), 10)
// Larger integers get thousands separators.
case int:
return FormatLogfmtInt64(int64(v))
case int32:
return FormatLogfmtInt64(int64(v))
case int64:
return FormatLogfmtInt64(v)
case uint:
return FormatLogfmtUint64(uint64(v))
case uint16:
return FormatLogfmtUint64(uint64(v))
case uint32:
return FormatLogfmtUint64(uint64(v))
case uint64:
Expand All @@ -384,15 +387,15 @@ func formatLogfmtValue(value interface{}, term bool) string {
}
}

// FormatLogfmtInt64 formats a potentially big number in a friendlier split format.
// FormatLogfmtInt64 formats n with thousand separators.
func FormatLogfmtInt64(n int64) string {
if n < 0 {
return formatLogfmtUint64(uint64(-n), true)
}
return formatLogfmtUint64(uint64(n), false)
}

// FormatLogfmtUint64 formats a potentially big number in a friendlier split format.
// FormatLogfmtUint64 formats n with thousand separators.
func FormatLogfmtUint64(n uint64) string {
return formatLogfmtUint64(n, false)
}
Expand Down Expand Up @@ -431,31 +434,38 @@ func formatLogfmtUint64(n uint64, neg bool) string {
return string(out[i+1:])
}

var big1000 = big.NewInt(1000)

// formatLogfmtBigInt formats a potentially gigantic number in a friendlier split
// format.
// formatLogfmtBigInt formats n with thousand separators.
func formatLogfmtBigInt(n *big.Int) string {
// Most number don't need fancy handling, just downcast
if n.IsUint64() {
return FormatLogfmtUint64(n.Uint64())
}
if n.IsInt64() {
return FormatLogfmtInt64(n.Int64())
}
// Ok, huge number needs huge effort
groups := make([]string, 0, 8) // random initial size to cover most cases
for n.Cmp(big1000) >= 0 {
_, mod := n.DivMod(n, big1000, nil)
groups = append(groups, fmt.Sprintf("%03d", mod))
}
groups = append(groups, n.String())

last := len(groups) - 1
for i := 0; i < len(groups)/2; i++ {
groups[i], groups[last-i] = groups[last-i], groups[i]
var (
text = n.String()
buf = make([]byte, len(text)+len(text)/3)
comma = 0
i = len(buf) - 1
)
for j := len(text) - 1; j >= 0; j, i = j-1, i-1 {
c := text[j]

switch {
case c == '-':
buf[i] = c
case comma == 3:
buf[i] = ','
i--
comma = 0
fallthrough
default:
buf[i] = c
comma++
}
}
return strings.Join(groups, ",")
return string(buf[i+1:])
}

// escapeString checks if the provided string needs escaping/quoting, and
Expand Down
20 changes: 20 additions & 0 deletions log/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package log

import (
"math"
"math/big"
"math/rand"
"testing"
)
Expand Down Expand Up @@ -58,6 +59,25 @@ func TestPrettyUint64(t *testing.T) {
}
}

func TestPrettyBigInt(t *testing.T) {
tests := []struct {
int string
s string
}{
{"111222333444555678999", "111,222,333,444,555,678,999"},
{"-111222333444555678999", "-111,222,333,444,555,678,999"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you add

		{"11122233344455567899900", "11,122,233,344,455,567,899,900"},
		{"-11122233344455567899900", "-11,122,233,344,455,567,899,900"},

the test will fail

{"11122233344455567899900", "11,122,233,344,455,567,899,900"},
{"-11122233344455567899900", "-11,122,233,344,455,567,899,900"},
}

for _, tt := range tests {
v, _ := new(big.Int).SetString(tt.int, 10)
if have := formatLogfmtBigInt(v); have != tt.s {
t.Errorf("invalid output %s, want %s", have, tt.s)
}
}
}

var sink string

func BenchmarkPrettyInt64Logfmt(b *testing.B) {
Expand Down