Performance improvements

This commit is contained in:
xuri 2020-04-02 00:41:14 +08:00
parent 59f6af21a3
commit 0f2a905324
10 changed files with 65 additions and 20 deletions

View File

@ -80,9 +80,10 @@ func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
// adjustRowDimensions provides a function to update row dimensions when // adjustRowDimensions provides a function to update row dimensions when
// inserting or deleting rows or columns. // inserting or deleting rows or columns.
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) { 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 { if newRow := r.R + offset; r.R >= row && newRow > 0 {
f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], newRow) f.ajustSingleRowDimensions(r, newRow)
} }
} }
} }

View File

@ -1288,7 +1288,7 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err
} }
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ { for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
deTwoCellAnchor = new(decodeTwoCellAnchor) 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 { Decode(deTwoCellAnchor); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err) err = fmt.Errorf("xml decode error: %s", err)
return return

View File

@ -234,10 +234,9 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
// replaceRelationshipsNameSpaceBytes provides a function to replace // replaceRelationshipsNameSpaceBytes provides a function to replace
// XML tags to self-closing for compatible Microsoft Office Excel 2007. // XML tags to self-closing for compatible Microsoft Office Excel 2007.
func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte { 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) var newXmlns = []byte(templateNamespaceIDMap)
contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1) return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
return contentMarshal
} }
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in // UpdateLinkedValue fix linked values within a spreadsheet are not updating in

42
lib.go
View File

@ -17,6 +17,7 @@ import (
"log" "log"
"strconv" "strconv"
"strings" "strings"
"unsafe"
) )
// ReadZipReader can be used to read an XLSX in memory without touching the // 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 { if row < 1 {
return "", newInvalidRowNumberError(row) 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 // 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) colname, err := ColumnNumberToName(col)
if err != nil { if err != nil {
// Error should never happens here.
return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err) return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
} }
return fmt.Sprintf("%s%d", colname, row), nil return fmt.Sprintf("%s%d", colname, row), nil
@ -235,11 +237,47 @@ func namespaceStrictToTransitional(content []byte) []byte {
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet, StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
} }
for s, n := range namespaceTranslationDic { for s, n := range namespaceTranslationDic {
content = bytes.Replace(content, []byte(s), []byte(n), -1) content = bytesReplace(content, stringToBytes(s), stringToBytes(n), -1)
} }
return content 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 // genSheetPasswd provides a method to generate password for worksheet
// protection by given plaintext. When an Excel sheet is being protected with // 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 // a password, a 16-bit (two byte) long hash is generated. To verify a

View File

@ -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))
}

View File

@ -510,7 +510,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
err = nil err = nil
for _, anchor := range deWsDr.TwoCellAnchor { for _, anchor := range deWsDr.TwoCellAnchor {
deTwoCellAnchor = new(decodeTwoCellAnchor) 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 { Decode(deTwoCellAnchor); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err) err = fmt.Errorf("xml decode error: %s", err)
return return

16
rows.go
View File

@ -424,14 +424,16 @@ func (f *File) RemoveRow(sheet string, row int) error {
if row > len(xlsx.SheetData.Row) { if row > len(xlsx.SheetData.Row) {
return f.adjustHelper(sheet, rows, row, -1) return f.adjustHelper(sheet, rows, row, -1)
} }
for rowIdx := range xlsx.SheetData.Row { keep := 0
if xlsx.SheetData.Row[rowIdx].R == row { for rowIdx := 0; rowIdx < len(xlsx.SheetData.Row); rowIdx++ {
xlsx.SheetData.Row = append(xlsx.SheetData.Row[:rowIdx], v := &xlsx.SheetData.Row[rowIdx]
xlsx.SheetData.Row[rowIdx+1:]...)[:len(xlsx.SheetData.Row)-1] if v.R != row {
xlsx.SheetData.Row[keep] = *v
keep++
}
}
xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
return f.adjustHelper(sheet, rows, row, -1) return f.adjustHelper(sheet, rows, row, -1)
}
}
return nil
} }
// InsertRow provides a function to insert a new row after given Excel row // InsertRow provides a function to insert a new row after given Excel row

View File

@ -206,9 +206,9 @@ func (f *File) setAppXML() {
// requirements about the structure of the input XML. This function is a // requirements about the structure of the input XML. This function is a
// horrible hack to fix that after the XML marshalling is completed. // horrible hack to fix that after the XML marshalling is completed.
func replaceRelationshipsBytes(content []byte) []byte { func replaceRelationshipsBytes(content []byte) []byte {
oldXmlns := []byte(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`) oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
newXmlns := []byte("r") newXmlns := stringToBytes("r")
return bytes.Replace(content, oldXmlns, newXmlns, -1) return bytesReplace(content, oldXmlns, newXmlns, -1)
} }
// SetActiveSheet provides function to set default active worksheet of XLSX by // SetActiveSheet provides function to set default active worksheet of XLSX by

View File

@ -516,7 +516,7 @@ func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup,
for idx, ext = range decodeExtLst.Ext { for idx, ext = range decodeExtLst.Ext {
if ext.URI == ExtURISparklineGroups { if ext.URI == ExtURISparklineGroups {
decodeSparklineGroups = new(decodeX14SparklineGroups) 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 { Decode(decodeSparklineGroups); err != nil && err != io.EOF {
return return
} }

View File

@ -365,7 +365,7 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
buf.WriteString(`>`) buf.WriteString(`>`)
if c.V != "" { if c.V != "" {
buf.WriteString(`<v>`) buf.WriteString(`<v>`)
xml.EscapeText(buf, []byte(c.V)) xml.EscapeText(buf, stringToBytes(c.V))
buf.WriteString(`</v>`) buf.WriteString(`</v>`)
} }
buf.WriteString(`</c>`) buf.WriteString(`</c>`)