Optimize ANSI code scanner

This change gives 5x speed improvement
This commit is contained in:
Junegunn Choi 2017-07-19 21:49:41 +09:00
parent a9e64efe45
commit 28810c178f
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627

View File

@ -44,7 +44,21 @@ func init() {
*/ */
// The following regular expression will include not all but most of the // The following regular expression will include not all but most of the
// frequently used ANSI sequences // frequently used ANSI sequences
ansiRegex = regexp.MustCompile("\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b.|[\x0e\x0f]|.\x08") ansiRegex = regexp.MustCompile("(?:\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b.|[\x0e\x0f]|.\x08)")
}
func findAnsiStart(str string) int {
idx := 0
for ; idx < len(str); idx++ {
b := str[idx]
if b == 0x1b || b == 0x0e || b == 0x0f {
return idx
}
if b == 0x08 && idx > 0 {
return idx - 1
}
}
return idx
} }
func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) { func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
@ -55,41 +69,61 @@ func extractColor(str string, state *ansiState, proc func(string, *ansiState) bo
offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state}) offsets = append(offsets, ansiOffset{[2]int32{0, 0}, *state})
} }
idx := 0 prevIdx := 0
for _, offset := range ansiRegex.FindAllStringIndex(str, -1) { runeCount := 0
prev := str[idx:offset[0]] for idx := 0; idx < len(str); {
output.WriteString(prev) idx += findAnsiStart(str[idx:])
// No sign of ANSI code
if idx == len(str) {
break
}
// Make sure that we found an ANSI code
offset := ansiRegex.FindStringIndex(str[idx:])
if offset == nil {
idx++
continue
}
offset[0] += idx
offset[1] += idx
idx = offset[1]
// Check if we should continue
prev := str[prevIdx:offset[0]]
if proc != nil && !proc(prev, state) { if proc != nil && !proc(prev, state) {
return "", nil, nil return "", nil, nil
} }
newState := interpretCode(str[offset[0]:offset[1]], state)
prevIdx = offset[1]
runeCount += utf8.RuneCountInString(prev)
output.WriteString(prev)
newState := interpretCode(str[offset[0]:offset[1]], state)
if !newState.equals(state) { if !newState.equals(state) {
if state != nil { if state != nil {
// Update last offset // Update last offset
(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes())) (&offsets[len(offsets)-1]).offset[1] = int32(runeCount)
} }
if newState.colored() { if newState.colored() {
// Append new offset // Append new offset
state = newState state = newState
newLen := int32(utf8.RuneCount(output.Bytes())) offsets = append(offsets, ansiOffset{[2]int32{int32(runeCount), int32(runeCount)}, *state})
offsets = append(offsets, ansiOffset{[2]int32{newLen, newLen}, *state})
} else { } else {
// Discard state // Discard state
state = nil state = nil
} }
} }
idx = offset[1]
} }
rest := str[idx:] rest := str[prevIdx:]
if len(rest) > 0 { if len(rest) > 0 {
output.WriteString(rest) output.WriteString(rest)
if state != nil { if state != nil {
// Update last offset // Update last offset
(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes())) runeCount += utf8.RuneCountInString(rest)
(&offsets[len(offsets)-1]).offset[1] = int32(runeCount)
} }
} }
if proc != nil { if proc != nil {