summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2020-04-02 00:41:14 +0800
committerxuri <xuri.me@gmail.com>2020-04-05 13:51:00 +0800
commit0f2a9053246c3ae45e6c7ba911a1fb135664abdf (patch)
tree7428d041bcd06956933003598fa56b0b0c246945
parent59f6af21a378fdde21422a92b79a7b03bba313d4 (diff)
Performance improvements
-rw-r--r--adjust.go5
-rw-r--r--drawing.go2
-rw-r--r--excelize.go5
-rw-r--r--lib.go42
-rw-r--r--lib_test.go5
-rw-r--r--picture.go2
-rw-r--r--rows.go14
-rw-r--r--sheet.go6
-rw-r--r--sparkline.go2
-rw-r--r--stream.go2
10 files changed, 65 insertions, 20 deletions
diff --git a/adjust.go b/adjust.go
index bedeec0..5056839 100644
--- a/adjust.go
+++ b/adjust.go
@@ -80,9 +80,10 @@ func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
// adjustRowDimensions provides a function to update row dimensions when
// inserting or deleting rows or columns.
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
- for i, r := range xlsx.SheetData.Row {
+ for i := range xlsx.SheetData.Row {
+ r := &xlsx.SheetData.Row[i]
if newRow := r.R + offset; r.R >= row && newRow > 0 {
- f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], newRow)
+ f.ajustSingleRowDimensions(r, newRow)
}
}
}
diff --git a/drawing.go b/drawing.go
index 13bdab4..e410599 100644
--- a/drawing.go
+++ b/drawing.go
@@ -1288,7 +1288,7 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err
}
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
deTwoCellAnchor = new(decodeTwoCellAnchor)
- if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
+ if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
diff --git a/excelize.go b/excelize.go
index 520cbb7..ae011d9 100644
--- a/excelize.go
+++ b/excelize.go
@@ -234,10 +234,9 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
// replaceRelationshipsNameSpaceBytes provides a function to replace
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
- var oldXmlns = []byte(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
+ var oldXmlns = stringToBytes(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
var newXmlns = []byte(templateNamespaceIDMap)
- contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1)
- return contentMarshal
+ return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
}
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
diff --git a/lib.go b/lib.go
index 2d606fa..83cdb4a 100644
--- a/lib.go
+++ b/lib.go
@@ -17,6 +17,7 @@ import (
"log"
"strconv"
"strings"
+ "unsafe"
)
// ReadZipReader can be used to read an XLSX in memory without touching the
@@ -103,7 +104,7 @@ func JoinCellName(col string, row int) (string, error) {
if row < 1 {
return "", newInvalidRowNumberError(row)
}
- return fmt.Sprintf("%s%d", normCol, row), nil
+ return normCol + strconv.Itoa(row), nil
}
// ColumnNameToNumber provides a function to convert Excel sheet column name
@@ -190,6 +191,7 @@ func CoordinatesToCellName(col, row int) (string, error) {
}
colname, err := ColumnNumberToName(col)
if err != nil {
+ // Error should never happens here.
return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
}
return fmt.Sprintf("%s%d", colname, row), nil
@@ -235,11 +237,47 @@ func namespaceStrictToTransitional(content []byte) []byte {
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
}
for s, n := range namespaceTranslationDic {
- content = bytes.Replace(content, []byte(s), []byte(n), -1)
+ content = bytesReplace(content, stringToBytes(s), stringToBytes(n), -1)
}
return content
}
+// stringToBytes cast a string to bytes pointer and assign the value of this
+// pointer.
+func stringToBytes(s string) []byte {
+ return *(*[]byte)(unsafe.Pointer(&s))
+}
+
+// bytesReplace replace old bytes with given new.
+func bytesReplace(s, old, new []byte, n int) []byte {
+ if n == 0 {
+ return s
+ }
+
+ if len(old) < len(new) {
+ return bytes.Replace(s, old, new, n)
+ }
+
+ if n < 0 {
+ n = len(s)
+ }
+
+ var wid, i, j, w int
+ for i, j = 0, 0; i < len(s) && j < n; j++ {
+ wid = bytes.Index(s[i:], old)
+ if wid < 0 {
+ break
+ }
+
+ w += copy(s[w:], s[i:i+wid])
+ w += copy(s[w:], new)
+ i += wid + len(old)
+ }
+
+ w += copy(s[w:], s[i:])
+ return s[0:w]
+}
+
// genSheetPasswd provides a method to generate password for worksheet
// protection by given plaintext. When an Excel sheet is being protected with
// a password, a 16-bit (two byte) long hash is generated. To verify a
diff --git a/lib_test.go b/lib_test.go
index 1c30c0e..4605e70 100644
--- a/lib_test.go
+++ b/lib_test.go
@@ -203,3 +203,8 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
}
}
}
+
+func TestBytesReplace(t *testing.T) {
+ s := []byte{0x01}
+ assert.EqualValues(t, s, bytesReplace(s, []byte{}, []byte{}, 0))
+}
diff --git a/picture.go b/picture.go
index ddc0480..fcdaa07 100644
--- a/picture.go
+++ b/picture.go
@@ -510,7 +510,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
err = nil
for _, anchor := range deWsDr.TwoCellAnchor {
deTwoCellAnchor = new(decodeTwoCellAnchor)
- if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>"))).
+ if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>"))).
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
diff --git a/rows.go b/rows.go
index d56c81c..8f000b3 100644
--- a/rows.go
+++ b/rows.go
@@ -424,14 +424,16 @@ func (f *File) RemoveRow(sheet string, row int) error {
if row > len(xlsx.SheetData.Row) {
return f.adjustHelper(sheet, rows, row, -1)
}
- for rowIdx := range xlsx.SheetData.Row {
- if xlsx.SheetData.Row[rowIdx].R == row {
- xlsx.SheetData.Row = append(xlsx.SheetData.Row[:rowIdx],
- xlsx.SheetData.Row[rowIdx+1:]...)[:len(xlsx.SheetData.Row)-1]
- return f.adjustHelper(sheet, rows, row, -1)
+ keep := 0
+ for rowIdx := 0; rowIdx < len(xlsx.SheetData.Row); rowIdx++ {
+ v := &xlsx.SheetData.Row[rowIdx]
+ if v.R != row {
+ xlsx.SheetData.Row[keep] = *v
+ keep++
}
}
- return nil
+ xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
+ return f.adjustHelper(sheet, rows, row, -1)
}
// InsertRow provides a function to insert a new row after given Excel row
diff --git a/sheet.go b/sheet.go
index 6ddd629..a3276c2 100644
--- a/sheet.go
+++ b/sheet.go
@@ -206,9 +206,9 @@ func (f *File) setAppXML() {
// requirements about the structure of the input XML. This function is a
// horrible hack to fix that after the XML marshalling is completed.
func replaceRelationshipsBytes(content []byte) []byte {
- oldXmlns := []byte(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
- newXmlns := []byte("r")
- return bytes.Replace(content, oldXmlns, newXmlns, -1)
+ oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
+ newXmlns := stringToBytes("r")
+ return bytesReplace(content, oldXmlns, newXmlns, -1)
}
// SetActiveSheet provides function to set default active worksheet of XLSX by
diff --git a/sparkline.go b/sparkline.go
index ef99da6..f1e1f40 100644
--- a/sparkline.go
+++ b/sparkline.go
@@ -516,7 +516,7 @@ func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup,
for idx, ext = range decodeExtLst.Ext {
if ext.URI == ExtURISparklineGroups {
decodeSparklineGroups = new(decodeX14SparklineGroups)
- if err = f.xmlNewDecoder(bytes.NewReader([]byte(ext.Content))).
+ if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes(ext.Content))).
Decode(decodeSparklineGroups); err != nil && err != io.EOF {
return
}
diff --git a/stream.go b/stream.go
index 98cf828..1af0b9f 100644
--- a/stream.go
+++ b/stream.go
@@ -365,7 +365,7 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
buf.WriteString(`>`)
if c.V != "" {
buf.WriteString(`<v>`)
- xml.EscapeText(buf, []byte(c.V))
+ xml.EscapeText(buf, stringToBytes(c.V))
buf.WriteString(`</v>`)
}
buf.WriteString(`</c>`)