diff options
author | xuri <xuri.me@gmail.com> | 2020-04-02 00:41:14 +0800 |
---|---|---|
committer | xuri <xuri.me@gmail.com> | 2020-04-05 13:51:00 +0800 |
commit | 0f2a9053246c3ae45e6c7ba911a1fb135664abdf (patch) | |
tree | 7428d041bcd06956933003598fa56b0b0c246945 | |
parent | 59f6af21a378fdde21422a92b79a7b03bba313d4 (diff) |
Performance improvements
-rw-r--r-- | adjust.go | 5 | ||||
-rw-r--r-- | drawing.go | 2 | ||||
-rw-r--r-- | excelize.go | 5 | ||||
-rw-r--r-- | lib.go | 42 | ||||
-rw-r--r-- | lib_test.go | 5 | ||||
-rw-r--r-- | picture.go | 2 | ||||
-rw-r--r-- | rows.go | 14 | ||||
-rw-r--r-- | sheet.go | 6 | ||||
-rw-r--r-- | sparkline.go | 2 | ||||
-rw-r--r-- | stream.go | 2 |
10 files changed, 65 insertions, 20 deletions
@@ -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) } } } @@ -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 @@ -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)) +} @@ -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 @@ -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 @@ -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 } @@ -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>`) |