Commit d4bdaf11 authored by Robert Griesemer's avatar Robert Griesemer

godoc: peephole optimization for generated HTML

When searching for regular expressions such as
".", there are many consecutive matches.
In the generated HTML, combine them instead of
generating a new <span> for each adjacent text
segment highlighting a match.

Massively reduces the size of the generated
HTML in those cases.

R=r, rsc
CC=golang-dev
https://golang.org/cl/3971041
parent 6f788e78
...@@ -62,12 +62,48 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, ...@@ -62,12 +62,48 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection,
if lw != nil { if lw != nil {
selections = append(selections, links) selections = append(selections, links)
} }
// compute the sequence of consecutive segment changes // compute the sequence of consecutive segment changes
changes := newMerger(selections) changes := newMerger(selections)
// The i'th bit in bitset indicates that the text // The i'th bit in bitset indicates that the text
// at the current offset is covered by selections[i]. // at the current offset is covered by selections[i].
bitset := 0 bitset := 0
lastOffs := 0 lastOffs := 0
// Text segments are written in a delayed fashion
// such that consecutive segments belonging to the
// same selection can be combined (peephole optimization).
// last describes the last segment which has not yet been written.
var last struct {
begin, end int // valid if begin < end
bitset int
}
// flush writes the last delayed text segment
flush := func() {
if last.begin < last.end {
sw(w, text[last.begin:last.end], last.bitset)
}
last.begin = last.end // invalidate last
}
// segment runs the segment [lastOffs, end) with the selection
// indicated by bitset through the segment peephole optimizer.
segment := func(end int) {
if lastOffs < end { // ignore empty segments
if last.end != lastOffs || last.bitset != bitset {
// the last segment is not adjacent or
// differs from the new one
flush()
// start a new segment
last.begin = lastOffs
}
last.end = end
last.bitset = bitset
}
}
for { for {
// get the next segment change // get the next segment change
index, offs, start := changes.next() index, offs, start := changes.next()
...@@ -81,14 +117,15 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, ...@@ -81,14 +117,15 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection,
// we have a link segment change: // we have a link segment change:
// format the previous selection segment, write the // format the previous selection segment, write the
// link tag and start a new selection segment // link tag and start a new selection segment
sw(w, text[lastOffs:offs], bitset) segment(offs)
flush()
lastOffs = offs lastOffs = offs
lw(w, offs, start) lw(w, offs, start)
} else { } else {
// we have a selection change: // we have a selection change:
// format the previous selection segment, determine // format the previous selection segment, determine
// the new selection bitset and start a new segment // the new selection bitset and start a new segment
sw(w, text[lastOffs:offs], bitset) segment(offs)
lastOffs = offs lastOffs = offs
mask := 1 << uint(index) mask := 1 << uint(index)
if start { if start {
...@@ -98,7 +135,8 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, ...@@ -98,7 +135,8 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection,
} }
} }
} }
sw(w, text[lastOffs:], bitset) segment(len(text))
flush()
} }
...@@ -283,17 +321,15 @@ var endTag = []byte(`</span>`) ...@@ -283,17 +321,15 @@ var endTag = []byte(`</span>`)
func selectionTag(w io.Writer, text []byte, selections int) { func selectionTag(w io.Writer, text []byte, selections int) {
if len(text) > 0 { if selections < len(startTags) {
if selections < len(startTags) { if tag := startTags[selections]; len(tag) > 0 {
if tag := startTags[selections]; len(tag) > 0 { w.Write(tag)
w.Write(tag) template.HTMLEscape(w, text)
template.HTMLEscape(w, text) w.Write(endTag)
w.Write(endTag) return
return
}
} }
template.HTMLEscape(w, text)
} }
template.HTMLEscape(w, text)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment