Pārlūkot izejas kodu

optimize SourceLine

Thibaut Rousseau 5 gadi atpakaļ
vecāks
revīzija
331a2a9283
2 mainītis faili ar 40 papildinājumiem un 8 dzēšanām
  1. 20 0
      error_test.go
  2. 20 8
      stackframe.go

+ 20 - 0
error_test.go

@@ -10,6 +10,26 @@ import (
 	"testing"
 )
 
+func BenchmarkStackFormat(b *testing.B) {
+	b.ReportAllocs()
+
+	for i := 0; i < b.N; i++ {
+		func() {
+			defer func() {
+				err := recover()
+				if err != 'a' {
+					b.Fatal(err)
+				}
+
+				e := Errorf("hi")
+				_ = string(e.Stack())
+			}()
+
+			a()
+		}()
+	}
+}
+
 func TestStackFormat(t *testing.T) {
 
 	defer func() {

+ 20 - 8
stackframe.go

@@ -1,9 +1,10 @@
 package errors
 
 import (
+	"bufio"
 	"bytes"
 	"fmt"
-	"io/ioutil"
+	"os"
 	"runtime"
 	"strings"
 )
@@ -62,18 +63,29 @@ func (frame *StackFrame) String() string {
 
 // SourceLine gets the line of code (from File and Line) of the original source if possible.
 func (frame *StackFrame) SourceLine() (string, error) {
-	data, err := ioutil.ReadFile(frame.File)
+	if frame.LineNumber <= 0 {
+		return "???", nil
+	}
 
+	file, err := os.Open(frame.File)
 	if err != nil {
 		return "", New(err)
 	}
-
-	lines := bytes.Split(data, []byte{'\n'})
-	if frame.LineNumber <= 0 || frame.LineNumber >= len(lines) {
-		return "???", nil
+	defer file.Close()
+
+	scanner := bufio.NewScanner(file)
+	currentLine := 1
+	for scanner.Scan() {
+		if currentLine == frame.LineNumber {
+			return string(bytes.Trim(scanner.Bytes(), " \t")), nil
+		}
+		currentLine++
 	}
-	// -1 because line-numbers are 1 based, but our array is 0 based
-	return string(bytes.Trim(lines[frame.LineNumber-1], " \t")), nil
+	if err := scanner.Err(); err != nil {
+		return "", New(err)
+	}
+
+	return "???", nil
 }
 
 func packageAndName(fn *runtime.Func) (string, string) {