MM-29981 - optimize markdown.Inspect (#16188)

* MM-29981 - optimize markdown.Inspect

- Cache regexp.MustCompile
- Reuse slice in MergeInlineText
- Remove pointer to slice in closeBlocks
- Pre-allocate slice in ParseLines
- Some more small cleanups

```
name       old time/op    new time/op    delta
Inspect-8    10.5µs ± 3%     6.6µs ± 1%  -37.59%  (p=0.000 n=10+7)

name       old alloc/op   new alloc/op   delta
Inspect-8    6.66kB ± 0%    3.22kB ± 0%  -51.62%  (p=0.000 n=10+9)

name       old allocs/op  new allocs/op  delta
Inspect-8       117 ± 0%        76 ± 0%  -35.04%  (p=0.000 n=10+10)
```

* fix lint

* remove ignore

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Agniva De Sarker
2020-11-17 09:12:41 +05:30
коммит произвёл GitHub
родитель 95221d9ace
Коммит eced0cdb72
6 изменённых файлов: 40 добавлений и 18 удалений

Просмотреть файл

@@ -14,6 +14,7 @@ import (
var ( var (
DefaultUrlSchemes = []string{"http", "https", "ftp", "mailto", "tel"} DefaultUrlSchemes = []string{"http", "https", "ftp", "mailto", "tel"}
wwwAutoLinkRegex = regexp.MustCompile(`^www\d{0,3}\.`)
) )
// Given a string with a w at the given position, tries to parse and return a range containing a www link. // Given a string with a w at the given position, tries to parse and return a range containing a www link.
@@ -30,7 +31,7 @@ func parseWWWAutolink(data string, position int) (Range, bool) {
} }
// Check that this starts with www // Check that this starts with www
if len(data)-position < 4 || !regexp.MustCompile(`^www\d{0,3}\.`).MatchString(data[position:]) { if len(data)-position < 4 || !wwwAutoLinkRegex.MatchString(data[position:]) {
return Range{}, false return Range{}, false
} }
@@ -59,9 +60,8 @@ func isAllowedBeforeWWWLink(c byte) bool {
switch c { switch c {
case '*', '_', '~', ')': case '*', '_', '~', ')':
return true return true
default:
return false
} }
return false
} }
// Given a string with a : at the given position, tried to parse and return a range containing a URL scheme // Given a string with a : at the given position, tried to parse and return a range containing a URL scheme
@@ -153,9 +153,8 @@ func checkDomain(data string, allowShort bool) int {
// this is called from parseWWWAutolink // this is called from parseWWWAutolink
if foundPeriod { if foundPeriod {
return i return i
} else {
return 0
} }
return 0
} }
// Returns true if the provided link starts with a valid character for a domain name. Equivalent to // Returns true if the provided link starts with a valid character for a domain name. Equivalent to
@@ -251,7 +250,6 @@ func canEndAutolink(c rune) bool {
switch c { switch c {
case '?', '!', '.', ',', ':', '*', '_', '~', '\'', '"': case '?', '!', '.', ',', ':', '*', '_', '~', '\'', '"':
return false return false
default:
return true
} }
return true
} }

Просмотреть файл

@@ -37,13 +37,14 @@ type Range struct {
End int End int
} }
func closeBlocks(blocks []Block, referenceDefinitions *[]*ReferenceDefinition) { func closeBlocks(blocks []Block, referenceDefinitions []*ReferenceDefinition) []*ReferenceDefinition {
for _, block := range blocks { for _, block := range blocks {
block.Close() block.Close()
if p, ok := block.(*Paragraph); ok && len(p.ReferenceDefinitions) > 0 { if p, ok := block.(*Paragraph); ok && len(p.ReferenceDefinitions) > 0 {
*referenceDefinitions = append(*referenceDefinitions, p.ReferenceDefinitions...) referenceDefinitions = append(referenceDefinitions, p.ReferenceDefinitions...)
} }
} }
return referenceDefinitions
} }
func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefinition) { func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefinition) {
@@ -78,7 +79,7 @@ func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefiniti
for i := lastMatchIndex; i >= 0; i-- { for i := lastMatchIndex; i >= 0; i-- {
if container, ok := openBlocks[i].(ContainerBlock); ok { if container, ok := openBlocks[i].(ContainerBlock); ok {
if addedBlocks := container.AddChild(newBlocks); addedBlocks != nil { if addedBlocks := container.AddChild(newBlocks); addedBlocks != nil {
closeBlocks(openBlocks[i+1:], &referenceDefinitions) referenceDefinitions = closeBlocks(openBlocks[i+1:], referenceDefinitions)
openBlocks = openBlocks[:i+1] openBlocks = openBlocks[:i+1]
openBlocks = append(openBlocks, addedBlocks...) openBlocks = append(openBlocks, addedBlocks...)
didAdd = true didAdd = true
@@ -98,7 +99,7 @@ func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefiniti
continue continue
} }
closeBlocks(openBlocks[lastMatchIndex+1:], &referenceDefinitions) referenceDefinitions = closeBlocks(openBlocks[lastMatchIndex+1:], referenceDefinitions)
openBlocks = openBlocks[:lastMatchIndex+1] openBlocks = openBlocks[:lastMatchIndex+1]
if openBlocks[lastMatchIndex].AddLine(indentation, r) { if openBlocks[lastMatchIndex].AddLine(indentation, r) {
@@ -109,7 +110,7 @@ func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefiniti
for i := lastMatchIndex; i >= 0; i-- { for i := lastMatchIndex; i >= 0; i-- {
if container, ok := openBlocks[i].(ContainerBlock); ok { if container, ok := openBlocks[i].(ContainerBlock); ok {
if newBlocks := container.AddChild([]Block{paragraph}); newBlocks != nil { if newBlocks := container.AddChild([]Block{paragraph}); newBlocks != nil {
closeBlocks(openBlocks[i+1:], &referenceDefinitions) referenceDefinitions = closeBlocks(openBlocks[i+1:], referenceDefinitions)
openBlocks = openBlocks[:i+1] openBlocks = openBlocks[:i+1]
openBlocks = append(openBlocks, newBlocks...) openBlocks = append(openBlocks, newBlocks...)
break break
@@ -119,7 +120,7 @@ func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefiniti
} }
} }
closeBlocks(openBlocks, &referenceDefinitions) referenceDefinitions = closeBlocks(openBlocks, referenceDefinitions)
return document, referenceDefinitions return document, referenceDefinitions
} }

Просмотреть файл

@@ -595,7 +595,7 @@ func ParseInlines(markdown string, ranges []Range, referenceDefinitions []*Refer
} }
func MergeInlineText(inlines []Inline) []Inline { func MergeInlineText(inlines []Inline) []Inline {
var ret []Inline ret := inlines[:0]
for i, v := range inlines { for i, v := range inlines {
// always add first node // always add first node
if i == 0 { if i == 0 {

Просмотреть файл

@@ -52,3 +52,24 @@ func TestInspect(t *testing.T) {
" Text", " Text",
}, visited) }, visited)
} }
var counterSink int
func BenchmarkInspect(b *testing.B) {
text := `Some standard piece of text.
Has a link [post](https://github.com) and also has a blockquote.
> This is a famous quote.
Some bold text **Text for markdown?** to go with it.
At the end, some more lines`
for i := 0; i < b.N; i++ {
Inspect(text, func(_ interface{}) bool {
counterSink++
return true
})
}
}

Просмотреть файл

@@ -3,13 +3,16 @@
package markdown package markdown
import "strings"
type Line struct { type Line struct {
Range Range
} }
func ParseLines(markdown string) (lines []Line) { func ParseLines(markdown string) []Line {
lineStartPosition := 0 lineStartPosition := 0
isAfterCarriageReturn := false isAfterCarriageReturn := false
lines := make([]Line, 0, strings.Count(markdown, "\n"))
for position, r := range markdown { for position, r := range markdown {
if r == '\n' { if r == '\n' {
lines = append(lines, Line{Range{lineStartPosition, position + 1}}) lines = append(lines, Line{Range{lineStartPosition, position + 1}})
@@ -23,5 +26,5 @@ func ParseLines(markdown string) (lines []Line) {
if lineStartPosition < len(markdown) { if lineStartPosition < len(markdown) {
lines = append(lines, Line{Range{lineStartPosition, len(markdown)}}) lines = append(lines, Line{Range{lineStartPosition, len(markdown)}})
} }
return return lines
} }

Просмотреть файл

@@ -23,9 +23,8 @@ func isWhitespace(c rune) bool {
switch c { switch c {
case ' ', '\t', '\n', '\u000b', '\u000c', '\r': case ' ', '\t', '\n', '\u000b', '\u000c', '\r':
return true return true
default:
return false
} }
return false
} }
func isWhitespaceByte(c byte) bool { func isWhitespaceByte(c byte) bool {