diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index d2fa81a7b3..37d82acbf4 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -249,6 +249,7 @@ var fmtTests = []struct { {"%.0c", '⌘', "⌘"}, // Specifying precision should have no effect. {"%3c", '⌘', " ⌘"}, {"%-3c", '⌘', "⌘ "}, + {"%c", uint64(0x100000000), "\ufffd"}, // Runes that are not printable. {"%c", '\U00000e00', "\u0e00"}, {"%c", '\U0010ffff', "\U0010ffff"}, diff --git a/src/fmt/format.go b/src/fmt/format.go index bd00e5a5e0..617f78f15e 100644 --- a/src/fmt/format.go +++ b/src/fmt/format.go @@ -461,13 +461,14 @@ func (f *fmt) fmtQ(s string) { // fmtC formats an integer as a Unicode character. // If the character is not valid Unicode, it will print '\ufffd'. func (f *fmt) fmtC(c uint64) { + // Explicitly check whether c exceeds utf8.MaxRune since the conversion + // of a uint64 to a rune may lose precision that indicates an overflow. r := rune(c) if c > utf8.MaxRune { r = utf8.RuneError } buf := f.intbuf[:0] - w := utf8.EncodeRune(buf[:utf8.UTFMax], r) - f.pad(buf[:w]) + f.pad(utf8.AppendRune(buf, r)) } // fmtQc formats an integer as a single-quoted, escaped Go character constant. diff --git a/src/fmt/print.go b/src/fmt/print.go index 8082d13874..4eabda1ce8 100644 --- a/src/fmt/print.go +++ b/src/fmt/print.go @@ -113,18 +113,7 @@ func (b *buffer) writeByte(c byte) { } func (bp *buffer) writeRune(r rune) { - if r < utf8.RuneSelf { - *bp = append(*bp, byte(r)) - return - } - - b := *bp - n := len(b) - for n+utf8.UTFMax > cap(b) { - b = append(b, 0) - } - w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r) - *bp = b[:n+w] + *bp = utf8.AppendRune(*bp, r) } // pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.