1
0
mirror of https://github.com/golang/go synced 2024-10-02 22:21:20 -06:00

go/printer: remove gratuitous string/[]byte conversions

Cleanup and slight performance improvement (1.5%).

Before (best of 3 runs):
printer.BenchmarkPrint	      50	  47377420 ns/op

After (best of 3 runs):
printer.BenchmarkPrint	      50	  46707180 ns/op

R=rsc
CC=golang-dev
https://golang.org/cl/5416049
This commit is contained in:
Robert Griesemer 2011-11-18 19:10:45 -08:00
parent dd731478b8
commit 17e493a2b1

View File

@ -362,25 +362,24 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
} }
} }
// TODO(gri): It should be possible to convert the code below from using
// []byte to string and in the process eliminate some conversions.
// Split comment text into lines // Split comment text into lines
func split(text []byte) [][]byte { // (using strings.Split(text, "\n") is significantly slower for
// this specific purpose, as measured with: gotest -bench=Print)
func split(text string) []string {
// count lines (comment text never ends in a newline) // count lines (comment text never ends in a newline)
n := 1 n := 1
for _, c := range text { for i := 0; i < len(text); i++ {
if c == '\n' { if text[i] == '\n' {
n++ n++
} }
} }
// split // split
lines := make([][]byte, n) lines := make([]string, n)
n = 0 n = 0
i := 0 i := 0
for j, c := range text { for j := 0; j < len(text); j++ {
if c == '\n' { if text[j] == '\n' {
lines[n] = text[i:j] // exclude newline lines[n] = text[i:j] // exclude newline
i = j + 1 // discard newline i = j + 1 // discard newline
n++ n++
@ -391,16 +390,18 @@ func split(text []byte) [][]byte {
return lines return lines
} }
func isBlank(s []byte) bool { // Returns true if s contains only white space
for _, b := range s { // (only tabs and blanks can appear in the printer's context).
if b > ' ' { func isBlank(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > ' ' {
return false return false
} }
} }
return true return true
} }
func commonPrefix(a, b []byte) []byte { func commonPrefix(a, b string) string {
i := 0 i := 0
for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') { for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {
i++ i++
@ -408,7 +409,7 @@ func commonPrefix(a, b []byte) []byte {
return a[0:i] return a[0:i]
} }
func stripCommonPrefix(lines [][]byte) { func stripCommonPrefix(lines []string) {
if len(lines) < 2 { if len(lines) < 2 {
return // at most one line - nothing to do return // at most one line - nothing to do
} }
@ -432,19 +433,21 @@ func stripCommonPrefix(lines [][]byte) {
// Note that the first and last line are never empty (they // Note that the first and last line are never empty (they
// contain the opening /* and closing */ respectively) and // contain the opening /* and closing */ respectively) and
// thus they can be ignored by the blank line check. // thus they can be ignored by the blank line check.
var prefix []byte var prefix string
if len(lines) > 2 { if len(lines) > 2 {
first := true
for i, line := range lines[1 : len(lines)-1] { for i, line := range lines[1 : len(lines)-1] {
switch { switch {
case isBlank(line): case isBlank(line):
lines[1+i] = nil // range starts at line 1 lines[1+i] = "" // range starts at line 1
case prefix == nil: case first:
prefix = commonPrefix(line, line) prefix = commonPrefix(line, line)
first = false
default: default:
prefix = commonPrefix(prefix, line) prefix = commonPrefix(prefix, line)
} }
} }
} else { // len(lines) == 2 } else { // len(lines) == 2, lines cannot be blank (contain /* and */)
line := lines[1] line := lines[1]
prefix = commonPrefix(line, line) prefix = commonPrefix(line, line)
} }
@ -453,7 +456,7 @@ func stripCommonPrefix(lines [][]byte) {
* Check for vertical "line of stars" and correct prefix accordingly. * Check for vertical "line of stars" and correct prefix accordingly.
*/ */
lineOfStars := false lineOfStars := false
if i := bytes.Index(prefix, []byte{'*'}); i >= 0 { if i := strings.Index(prefix, "*"); i >= 0 {
// Line of stars present. // Line of stars present.
if i > 0 && prefix[i-1] == ' ' { if i > 0 && prefix[i-1] == ' ' {
i-- // remove trailing blank from prefix so stars remain aligned i-- // remove trailing blank from prefix so stars remain aligned
@ -501,7 +504,7 @@ func stripCommonPrefix(lines [][]byte) {
} }
// Shorten the computed common prefix by the length of // Shorten the computed common prefix by the length of
// suffix, if it is found as suffix of the prefix. // suffix, if it is found as suffix of the prefix.
if bytes.HasSuffix(prefix, suffix) { if strings.HasSuffix(prefix, string(suffix)) {
prefix = prefix[0 : len(prefix)-len(suffix)] prefix = prefix[0 : len(prefix)-len(suffix)]
} }
} }
@ -511,19 +514,18 @@ func stripCommonPrefix(lines [][]byte) {
// with the opening /*, otherwise align the text with the other // with the opening /*, otherwise align the text with the other
// lines. // lines.
last := lines[len(lines)-1] last := lines[len(lines)-1]
closing := []byte("*/") closing := "*/"
i := bytes.Index(last, closing) i := strings.Index(last, closing) // i >= 0 (closing is always present)
if isBlank(last[0:i]) { if isBlank(last[0:i]) {
// last line only contains closing */ // last line only contains closing */
var sep []byte
if lineOfStars { if lineOfStars {
// insert an aligning blank closing = " */" // add blank to align final star
sep = []byte{' '}
} }
lines[len(lines)-1] = bytes.Join([][]byte{prefix, closing}, sep) lines[len(lines)-1] = prefix + closing
} else { } else {
// last line contains more comment text - assume // last line contains more comment text - assume
// it is aligned like the other lines // it is aligned like the other lines and include
// in prefix computation
prefix = commonPrefix(prefix, last) prefix = commonPrefix(prefix, last)
} }
@ -549,9 +551,9 @@ func (p *printer) writeComment(comment *ast.Comment) {
// update our own idea of the file and line number // update our own idea of the file and line number
// accordingly, after printing the directive. // accordingly, after printing the directive.
file := pos[:i] file := pos[:i]
line, _ := strconv.Atoi(string(pos[i+1:])) line, _ := strconv.Atoi(pos[i+1:])
defer func() { defer func() {
p.pos.Filename = string(file) p.pos.Filename = file
p.pos.Line = line p.pos.Line = line
p.pos.Column = 1 p.pos.Column = 1
}() }()
@ -566,7 +568,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
// for /*-style comments, print line by line and let the // for /*-style comments, print line by line and let the
// write function take care of the proper indentation // write function take care of the proper indentation
lines := split([]byte(text)) lines := split(text)
stripCommonPrefix(lines) stripCommonPrefix(lines)
// write comment lines, separated by formfeed, // write comment lines, separated by formfeed,
@ -579,7 +581,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
pos = p.pos pos = p.pos
} }
if len(line) > 0 { if len(line) > 0 {
p.writeItem(pos, p.escape(string(line))) p.writeItem(pos, p.escape(line))
} }
} }
} }