Skip to content
This repository was archived by the owner on Aug 21, 2020. It is now read-only.

Commit 0def143

Browse files
committed
Work around long string concatenation with writeWrappedString
The compiler has trouble compiling the concatenation of many strings, s0 + s1 + s2 + ... + sN, for large N. We insert redundant, explicit parentheses to work around that, lowering the N at any given step. This bug is golang/go#16394 and the fix is taken from golang/text@5c6cf4f9a2. Fixes #8
1 parent ac34f9a commit 0def143

File tree

3 files changed

+112
-11
lines changed

3 files changed

+112
-11
lines changed

issues_test.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2017 Tom Thorogood. All rights reserved.
2+
// Use of this source code is governed by a Modified
3+
// BSD License that can be found in the LICENSE file.
4+
5+
package bindata
6+
7+
import (
8+
"bytes"
9+
"context"
10+
"io"
11+
"io/ioutil"
12+
"os"
13+
"os/exec"
14+
"path"
15+
"testing"
16+
"time"
17+
)
18+
19+
type loggerWriter struct{ testing.TB }
20+
21+
func (w *loggerWriter) Write(p []byte) (n int, err error) {
22+
w.Log(string(bytes.TrimRight(p, "\r\n")))
23+
return len(p), nil
24+
}
25+
26+
func TestIssue8(t *testing.T) {
27+
// This test case covers https://github.com/tmthrgd/go-bindata/issues/8.
28+
//
29+
// It generates a file with ~43,000 string concatenations which
30+
// triggers https://golang.org/issue/16394.
31+
32+
if testing.Short() {
33+
t.Skip("skipping in short mode")
34+
}
35+
36+
dir, err := ioutil.TempDir("", "go-bindata-test-")
37+
if err != nil {
38+
t.Fatal(err)
39+
}
40+
defer os.RemoveAll(dir)
41+
42+
f, err := os.Create(path.Join(dir, "issue-8.go"))
43+
if err != nil {
44+
t.Fatal(err)
45+
}
46+
defer f.Close()
47+
48+
if err = (Files{
49+
&testFile{
50+
path: "issue-8.bin",
51+
size: 1 << 20,
52+
},
53+
}).Generate(f, &GenerateOptions{
54+
Package: "main",
55+
}); err != nil {
56+
t.Fatal(err)
57+
}
58+
59+
if _, err = io.WriteString(f, "\nfunc main() {}\n"); err != nil {
60+
t.Fatal(err)
61+
}
62+
63+
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
64+
defer cancel()
65+
66+
cmd := exec.CommandContext(ctx, "go", "build", ".")
67+
68+
cmd.Dir = dir
69+
cmd.Env = os.Environ()
70+
71+
cmd.Stderr = &loggerWriter{t}
72+
cmd.Stdout = cmd.Stderr
73+
74+
if err = cmd.Run(); err != nil {
75+
t.Fatal(err)
76+
}
77+
}

release.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"compress/flate"
1010
"io"
1111
"path"
12+
"strings"
1213
"sync"
1314
"text/template"
1415
)
@@ -22,6 +23,8 @@ func writeWrappedString(write func(io.Writer) error, indent string, wrapAt int)
2223
bufPool.Put(buf)
2324
}()
2425

26+
buf.WriteString("(\"\" +\n")
27+
buf.WriteString(indent)
2528
buf.WriteByte('"')
2629

2730
if err := write(&stringWriter{
@@ -32,8 +35,15 @@ func writeWrappedString(write func(io.Writer) error, indent string, wrapAt int)
3235
return "", err
3336
}
3437

35-
buf.WriteByte('"')
36-
return buf.String(), nil
38+
buf.WriteString("\")")
39+
40+
s := buf.String()
41+
42+
if strings.IndexByte(s[1:], '(') == -1 {
43+
s = s[1 : len(s)-1]
44+
}
45+
46+
return s, nil
3747
}
3848

3949
func init() {
@@ -209,11 +219,9 @@ var _bindata = map[string]*asset{
209219
orig: {{printf "%q" .File.Name}},
210220
{{- end}}
211221
data: {{if $.Compress -}}
212-
"" +
213222
{{flate . "\t\t\t" 24}}
214223
{{- else -}}
215-
"" +
216-
{{read . "\t\t\t" 24 -}}
224+
{{read . "\t\t\t" 24}}
217225
{{- end}},
218226
219227
{{- if or $.Metadata $.Compress -}}
@@ -234,8 +242,7 @@ var _bindata = map[string]*asset{
234242
{{- end -}}
235243
236244
{{- if $.Hash}}
237-
hash: "" +
238-
{{wrap .Hash "\t\t\t" 24}},
245+
hash: {{wrap .Hash "\t\t\t" 24}},
239246
{{- end}}
240247
},
241248
{{end -}}

stringwriter.go

+21-4
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ package bindata
77
import "io"
88

99
var (
10-
stringWriterLinePrefix = []byte(`"`)
11-
stringWriterLineSuffix = []byte("\" +\n")
10+
stringWriterLinePrefix = []byte(`"`)
11+
stringWriterLineSuffix = []byte("\" +\n")
12+
stringWriterParensLineSuffix = []byte("\") + (\"\" +\n")
1213
)
1314

1415
type stringWriter struct {
1516
io.Writer
1617
Indent string
1718
WrapAt int
18-
c int
19+
c, l int
1920
}
2021

2122
func (w *stringWriter) Write(p []byte) (n int, err error) {
@@ -37,7 +38,23 @@ func (w *stringWriter) Write(p []byte) (n int, err error) {
3738
continue
3839
}
3940

40-
if _, err = w.Writer.Write(stringWriterLineSuffix); err != nil {
41+
w.l++
42+
43+
suffix := stringWriterLineSuffix
44+
if w.l%500 == 0 {
45+
// As per https://golang.org/issue/18078, the compiler has trouble
46+
// compiling the concatenation of many strings, s0 + s1 + s2 + ... + sN,
47+
// for large N. We insert redundant, explicit parentheses to work around
48+
// that, lowering the N at any given step: (s0 + s1 + ... + s499) + (s500 +
49+
// ... + s1999) + etc + (etc + ... + sN).
50+
//
51+
// This fix was taken from the fix applied to x/text in
52+
// https://github.com/golang/text/commit/5c6cf4f9a2.
53+
54+
suffix = stringWriterParensLineSuffix
55+
}
56+
57+
if _, err = w.Writer.Write(suffix); err != nil {
4158
return
4259
}
4360

0 commit comments

Comments
 (0)