Skip to content

Commit 9904fe7

Browse files
committed
Merge branch 'performance'
name old time/op new time/op delta CSS/sample_bootstrap.css-4 3.14ms ± 4% 2.26ms ± 0% -28.26% (p=0.008 n=5+5) CSS/sample_gumby.css-4 4.30ms ± 2% 2.92ms ± 1% -32.13% (p=0.008 n=5+5) HTML/sample_amazon.html-4 3.40ms ± 1% 2.33ms ± 2% -31.57% (p=0.008 n=5+5) HTML/sample_bbc.html-4 1.39ms ± 2% 1.02ms ± 1% -26.61% (p=0.008 n=5+5) HTML/sample_blogpost.html-4 224µs ± 1% 171µs ± 2% -23.95% (p=0.008 n=5+5) HTML/sample_es6.html-4 18.2ms ± 1% 14.5ms ± 0% -20.51% (p=0.016 n=5+4) HTML/sample_stackoverflow.html-4 3.13ms ± 1% 2.41ms ± 1% -22.89% (p=0.008 n=5+5) HTML/sample_wikipedia.html-4 6.16ms ± 1% 4.76ms ± 0% -22.76% (p=0.008 n=5+5) JS/sample_ace.js-4 10.0ms ± 1% 7.4ms ± 0% -25.59% (p=0.008 n=5+5) JS/sample_dot.js-4 90.5µs ± 1% 63.7µs ± 0% -29.64% (p=0.008 n=5+5) JS/sample_jquery.js-4 4.02ms ± 0% 2.99ms ± 0% -25.59% (p=0.008 n=5+5) JS/sample_jqueryui.js-4 7.98ms ± 1% 5.92ms ± 2% -25.80% (p=0.008 n=5+5) JS/sample_moment.js-4 1.47ms ± 1% 1.09ms ± 1% -25.81% (p=0.008 n=5+5) JSON/sample_large.json-4 5.03ms ± 0% 2.95ms ± 0% -41.39% (p=0.016 n=5+4) JSON/sample_testsuite.json-4 2.98ms ± 0% 1.51ms ± 1% -49.49% (p=0.016 n=4+5) JSON/sample_twitter.json-4 11.4µs ± 0% 6.8µs ± 1% -40.87% (p=0.016 n=4+5) SVG/sample_arctic.svg-4 65.5ms ± 0% 62.3ms ± 1% -4.95% (p=0.016 n=4+5) SVG/sample_gopher.svg-4 230µs ± 0% 218µs ± 0% -5.24% (p=0.029 n=4+4) SVG/sample_usa.svg-4 35.4ms ± 4% 33.1ms ± 3% -6.32% (p=0.008 n=5+5) XML/sample_books.xml-4 47.8µs ± 0% 36.2µs ± 0% -24.20% (p=0.016 n=5+4) XML/sample_catalog.xml-4 20.5µs ± 1% 14.9µs ± 0% -27.16% (p=0.008 n=5+5) XML/sample_omg.xml-4 9.09ms ± 0% 6.31ms ± 1% -30.63% (p=0.016 n=4+5) name old speed new speed delta CSS/sample_bootstrap.css-4 43.6MB/s ± 4% 60.8MB/s ± 0% +39.30% (p=0.008 n=5+5) CSS/sample_gumby.css-4 43.3MB/s ± 2% 63.9MB/s ± 1% +47.34% (p=0.008 n=5+5) HTML/sample_amazon.html-4 139MB/s ± 1% 203MB/s ± 2% +46.15% (p=0.008 n=5+5) HTML/sample_bbc.html-4 82.7MB/s ± 2% 112.7MB/s ± 1% +36.25% (p=0.008 n=5+5) HTML/sample_blogpost.html-4 93.3MB/s ± 1% 122.7MB/s ± 2% +31.49% (p=0.008 n=5+5) HTML/sample_es6.html-4 56.2MB/s ± 1% 70.7MB/s ± 0% +25.79% (p=0.016 n=5+4) HTML/sample_stackoverflow.html-4 65.7MB/s ± 1% 85.2MB/s ± 1% +29.68% (p=0.008 n=5+5) HTML/sample_wikipedia.html-4 72.3MB/s ± 1% 93.6MB/s ± 0% +29.47% (p=0.008 n=5+5) JS/sample_ace.js-4 64.7MB/s ± 1% 86.9MB/s ± 0% +34.39% (p=0.008 n=5+5) JS/sample_dot.js-4 57.0MB/s ± 1% 81.0MB/s ± 0% +42.11% (p=0.008 n=5+5) JS/sample_jquery.js-4 61.6MB/s ± 0% 82.8MB/s ± 0% +34.38% (p=0.008 n=5+5) JS/sample_jqueryui.js-4 58.9MB/s ± 1% 79.3MB/s ± 2% +34.78% (p=0.008 n=5+5) JS/sample_moment.js-4 67.7MB/s ± 1% 91.2MB/s ± 1% +34.80% (p=0.008 n=5+5) JSON/sample_large.json-4 151MB/s ± 0% 258MB/s ± 0% +70.62% (p=0.016 n=5+4) JSON/sample_testsuite.json-4 231MB/s ± 0% 457MB/s ± 1% +97.98% (p=0.016 n=4+5) JSON/sample_twitter.json-4 133MB/s ± 0% 226MB/s ± 1% +69.11% (p=0.016 n=4+5) SVG/sample_arctic.svg-4 22.4MB/s ± 0% 23.6MB/s ± 1% +5.20% (p=0.016 n=4+5) SVG/sample_gopher.svg-4 25.3MB/s ± 0% 26.7MB/s ± 0% +5.52% (p=0.029 n=4+4) SVG/sample_usa.svg-4 28.9MB/s ± 4% 30.9MB/s ± 3% +6.75% (p=0.008 n=5+5) XML/sample_books.xml-4 92.7MB/s ± 0% 122.3MB/s ± 0% +31.93% (p=0.016 n=5+4) XML/sample_catalog.xml-4 94.5MB/s ± 1% 129.7MB/s ± 0% +37.29% (p=0.008 n=5+5) XML/sample_omg.xml-4 125MB/s ± 0% 180MB/s ± 1% +44.15% (p=0.016 n=4+5)
2 parents c6580e5 + f600eb6 commit 9904fe7

File tree

17 files changed

+237
-161
lines changed

17 files changed

+237
-161
lines changed

benchmarks/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
*.test
22
old
3+
old_*
34
new
5+
new_*
46
cpu
7+
cpu_*
58
mem
9+
mem_*

common_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ func TestContentType(t *testing.T) {
2424
}
2525
for _, tt := range contentTypeTests {
2626
t.Run(tt.contentType, func(t *testing.T) {
27-
test.Minify(t, tt.contentType, nil, string(ContentType([]byte(tt.contentType))), tt.expected)
27+
contentType := ContentType([]byte(tt.contentType))
28+
test.Minify(t, tt.contentType, nil, string(contentType), tt.expected)
2829
})
2930
}
3031
}
@@ -55,7 +56,8 @@ func TestDataURI(t *testing.T) {
5556
})
5657
for _, tt := range dataURITests {
5758
t.Run(tt.dataURI, func(t *testing.T) {
58-
test.Minify(t, tt.dataURI, nil, string(DataURI(m, []byte(tt.dataURI))), tt.expected)
59+
dataURI := DataURI(m, []byte(tt.dataURI))
60+
test.Minify(t, tt.dataURI, nil, string(dataURI), tt.expected)
5961
})
6062
}
6163
}
@@ -126,7 +128,8 @@ func TestNumber(t *testing.T) {
126128
}
127129
for _, tt := range numberTests {
128130
t.Run(tt.number, func(t *testing.T) {
129-
test.Minify(t, tt.number, nil, string(Number([]byte(tt.number), -1)), tt.expected)
131+
number := Number([]byte(tt.number), -1)
132+
test.Minify(t, tt.number, nil, string(number), tt.expected)
130133
})
131134
}
132135
}
@@ -155,7 +158,8 @@ func TestNumberTruncate(t *testing.T) {
155158
}
156159
for _, tt := range numberTests {
157160
t.Run(tt.number, func(t *testing.T) {
158-
test.Minify(t, tt.number, nil, string(Number([]byte(tt.number), tt.truncate)), tt.expected, "truncate to", tt.truncate)
161+
number := Number([]byte(tt.number), tt.truncate)
162+
test.Minify(t, tt.number, nil, string(number), tt.expected, "truncate to", tt.truncate)
159163
})
160164
}
161165
}

css/css.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var (
1616
spaceBytes = []byte(" ")
1717
colonBytes = []byte(":")
1818
semicolonBytes = []byte(";")
19+
commaBytes = []byte(",")
1920
leftBracketBytes = []byte("{")
2021
rightBracketBytes = []byte("}")
2122
zeroBytes = []byte("0")
@@ -123,6 +124,13 @@ func (c *cssMinifier) minifyGrammar() error {
123124
if _, err := c.w.Write(leftBracketBytes); err != nil {
124125
return err
125126
}
127+
} else if gt == css.QualifiedRuleGrammar {
128+
if err := c.minifySelectors(data, c.p.Values()); err != nil {
129+
return err
130+
}
131+
if _, err := c.w.Write(commaBytes); err != nil {
132+
return err
133+
}
126134
} else if gt == css.BeginRulesetGrammar {
127135
if err := c.minifySelectors(data, c.p.Values()); err != nil {
128136
return err

css/css_test.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package css // import "github.com/tdewolff/minify/css"
22

33
import (
44
"bytes"
5+
"fmt"
56
"os"
67
"testing"
78

@@ -54,9 +55,12 @@ func TestCSS(t *testing.T) {
5455

5556
m := minify.New()
5657
for _, tt := range cssTests {
57-
r := bytes.NewBufferString(tt.css)
58-
w := &bytes.Buffer{}
59-
test.Minify(t, tt.css, Minify(m, w, r, nil), w.String(), tt.expected)
58+
t.Run(tt.css, func(t *testing.T) {
59+
r := bytes.NewBufferString(tt.css)
60+
w := &bytes.Buffer{}
61+
err := Minify(m, w, r, nil)
62+
test.Minify(t, tt.css, err, w.String(), tt.expected)
63+
})
6064
}
6165
}
6266

@@ -168,17 +172,21 @@ func TestCSSInline(t *testing.T) {
168172
m := minify.New()
169173
params := map[string]string{"inline": "1"}
170174
for _, tt := range cssTests {
171-
r := bytes.NewBufferString(tt.css)
172-
w := &bytes.Buffer{}
173-
test.Minify(t, tt.css, Minify(m, w, r, params), w.String(), tt.expected)
175+
t.Run(tt.css, func(t *testing.T) {
176+
r := bytes.NewBufferString(tt.css)
177+
w := &bytes.Buffer{}
178+
err := Minify(m, w, r, params)
179+
test.Minify(t, tt.css, err, w.String(), tt.expected)
180+
})
174181
}
175182
}
176183

177184
func TestReaderErrors(t *testing.T) {
178-
m := minify.New()
179185
r := test.NewErrorReader(0)
180186
w := &bytes.Buffer{}
181-
test.Error(t, Minify(m, w, r, nil), test.ErrPlain, "return error at first read")
187+
m := minify.New()
188+
err := Minify(m, w, r, nil)
189+
test.T(t, err, test.ErrPlain, "return error at first read")
182190
}
183191

184192
func TestWriterErrors(t *testing.T) {
@@ -204,9 +212,12 @@ func TestWriterErrors(t *testing.T) {
204212
m := minify.New()
205213
for _, tt := range errorTests {
206214
for _, n := range tt.n {
207-
r := bytes.NewBufferString(tt.css)
208-
w := test.NewErrorWriter(n)
209-
test.Error(t, Minify(m, w, r, nil), test.ErrPlain, "return error at write", n, "in", tt.css)
215+
t.Run(fmt.Sprint(tt.css, " ", tt.n), func(t *testing.T) {
216+
r := bytes.NewBufferString(tt.css)
217+
w := test.NewErrorWriter(n)
218+
err := Minify(m, w, r, nil)
219+
test.T(t, err, test.ErrPlain)
220+
})
210221
}
211222
}
212223
}

html/buffer.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ type TokenBuffer struct {
2323
pos int
2424

2525
attrBuffer []*Token
26-
prevN int
2726
}
2827

2928
// NewTokenBuffer returns a new TokenBuffer.
@@ -92,16 +91,13 @@ func (z *TokenBuffer) Peek(pos int) *Token {
9291

9392
// Shift returns the first element and advances position.
9493
func (z *TokenBuffer) Shift() *Token {
95-
z.l.Free(z.prevN)
9694
if z.pos >= len(z.buf) {
9795
t := &z.buf[:1][0]
9896
z.read(t)
99-
z.prevN = len(t.Data)
10097
return t
10198
}
10299
t := &z.buf[z.pos]
103100
z.pos++
104-
z.prevN = len(t.Data)
105101
return t
106102
}
107103

html/html.go

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -275,30 +275,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
275275
}
276276

277277
if hasAttributes {
278-
// rewrite attributes with interdependent conditions
279-
if t.Hash == html.A {
280-
attrs := tb.Attributes(html.Id, html.Name, html.Href)
281-
if id, name := attrs[0], attrs[1]; id != nil && name != nil && bytes.Equal(id.AttrVal, name.AttrVal) {
282-
name.Text = nil
283-
}
284-
if href := attrs[2]; href != nil {
285-
if len(href.AttrVal) > 5 && parse.EqualFold(href.AttrVal[:4], httpBytes) {
286-
if href.AttrVal[4] == ':' {
287-
if m.URL != nil && m.URL.Scheme == "http" {
288-
href.AttrVal = href.AttrVal[5:]
289-
} else {
290-
parse.ToLower(href.AttrVal[:4])
291-
}
292-
} else if (href.AttrVal[4] == 's' || href.AttrVal[4] == 'S') && href.AttrVal[5] == ':' {
293-
if m.URL != nil && m.URL.Scheme == "https" {
294-
href.AttrVal = href.AttrVal[6:]
295-
} else {
296-
parse.ToLower(href.AttrVal[:5])
297-
}
298-
}
299-
}
300-
}
301-
} else if t.Hash == html.Meta {
278+
if t.Hash == html.Meta {
302279
attrs := tb.Attributes(html.Content, html.Http_Equiv, html.Charset, html.Name)
303280
if content := attrs[0]; content != nil {
304281
if httpEquiv := attrs[1]; httpEquiv != nil {
@@ -351,6 +328,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
351328
}
352329

353330
// write attributes
331+
htmlEqualIdName := false
354332
for {
355333
attr := *tb.Shift()
356334
if attr.TokenType != html.AttributeToken {
@@ -359,6 +337,18 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
359337
continue // removed attribute
360338
}
361339

340+
if t.Hash == html.A && (attr.Hash == html.Id || attr.Hash == html.Name) {
341+
if attr.Hash == html.Id {
342+
if name := tb.Attributes(html.Name)[0]; name != nil && bytes.Equal(attr.AttrVal, name.AttrVal) {
343+
htmlEqualIdName = true
344+
}
345+
} else if htmlEqualIdName {
346+
continue
347+
} else if id := tb.Attributes(html.Id)[0]; id != nil && bytes.Equal(id.AttrVal, attr.AttrVal) {
348+
continue
349+
}
350+
}
351+
362352
val := attr.AttrVal
363353
if len(val) == 0 && (attr.Hash == html.Class ||
364354
attr.Hash == html.Dir ||
@@ -400,6 +390,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
400390
attr.Hash == html.Media && t.Hash == html.Style && bytes.Equal(val, []byte("all"))) {
401391
continue
402392
}
393+
403394
// CSS and JS minifiers for attribute inline code
404395
if attr.Hash == html.Style {
405396
attrMinifyBuffer.Reset()
@@ -425,24 +416,21 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
425416
continue
426417
}
427418
} else if len(val) > 5 && attr.Traits&urlAttr != 0 { // anchors are already handled
428-
if t.Hash != html.A {
429-
if parse.EqualFold(val[:4], httpBytes) {
430-
if val[4] == ':' {
431-
if m.URL != nil && m.URL.Scheme == "http" {
432-
val = val[5:]
433-
} else {
434-
parse.ToLower(val[:4])
435-
}
436-
} else if (val[4] == 's' || val[4] == 'S') && val[5] == ':' {
437-
if m.URL != nil && m.URL.Scheme == "https" {
438-
val = val[6:]
439-
} else {
440-
parse.ToLower(val[:5])
441-
}
419+
if parse.EqualFold(val[:4], httpBytes) {
420+
if val[4] == ':' {
421+
if m.URL != nil && m.URL.Scheme == "http" {
422+
val = val[5:]
423+
} else {
424+
parse.ToLower(val[:4])
425+
}
426+
} else if (val[4] == 's' || val[4] == 'S') && val[5] == ':' {
427+
if m.URL != nil && m.URL.Scheme == "https" {
428+
val = val[6:]
429+
} else {
430+
parse.ToLower(val[:5])
442431
}
443432
}
444-
}
445-
if parse.EqualFold(val[:5], dataSchemeBytes) {
433+
} else if parse.EqualFold(val[:5], dataSchemeBytes) {
446434
val = minify.DataURI(m, val)
447435
}
448436
}

0 commit comments

Comments
 (0)