Skip to content

Commit 0f2a905

Browse files
committed
Performance improvements
1 parent 59f6af2 commit 0f2a905

10 files changed

+65
-20
lines changed

adjust.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
8080
// adjustRowDimensions provides a function to update row dimensions when
8181
// inserting or deleting rows or columns.
8282
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
83-
for i, r := range xlsx.SheetData.Row {
83+
for i := range xlsx.SheetData.Row {
84+
r := &xlsx.SheetData.Row[i]
8485
if newRow := r.R + offset; r.R >= row && newRow > 0 {
85-
f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], newRow)
86+
f.ajustSingleRowDimensions(r, newRow)
8687
}
8788
}
8889
}

drawing.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,7 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err
12881288
}
12891289
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
12901290
deTwoCellAnchor = new(decodeTwoCellAnchor)
1291-
if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
1291+
if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
12921292
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
12931293
err = fmt.Errorf("xml decode error: %s", err)
12941294
return

excelize.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,9 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
234234
// replaceRelationshipsNameSpaceBytes provides a function to replace
235235
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
236236
func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
237-
var oldXmlns = []byte(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
237+
var oldXmlns = stringToBytes(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
238238
var newXmlns = []byte(templateNamespaceIDMap)
239-
contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1)
240-
return contentMarshal
239+
return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
241240
}
242241

243242
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in

lib.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"log"
1818
"strconv"
1919
"strings"
20+
"unsafe"
2021
)
2122

2223
// 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) {
103104
if row < 1 {
104105
return "", newInvalidRowNumberError(row)
105106
}
106-
return fmt.Sprintf("%s%d", normCol, row), nil
107+
return normCol + strconv.Itoa(row), nil
107108
}
108109

109110
// ColumnNameToNumber provides a function to convert Excel sheet column name
@@ -190,6 +191,7 @@ func CoordinatesToCellName(col, row int) (string, error) {
190191
}
191192
colname, err := ColumnNumberToName(col)
192193
if err != nil {
194+
// Error should never happens here.
193195
return "", fmt.Errorf("invalid cell coordinates [%d, %d]: %v", col, row, err)
194196
}
195197
return fmt.Sprintf("%s%d", colname, row), nil
@@ -235,11 +237,47 @@ func namespaceStrictToTransitional(content []byte) []byte {
235237
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
236238
}
237239
for s, n := range namespaceTranslationDic {
238-
content = bytes.Replace(content, []byte(s), []byte(n), -1)
240+
content = bytesReplace(content, stringToBytes(s), stringToBytes(n), -1)
239241
}
240242
return content
241243
}
242244

245+
// stringToBytes cast a string to bytes pointer and assign the value of this
246+
// pointer.
247+
func stringToBytes(s string) []byte {
248+
return *(*[]byte)(unsafe.Pointer(&s))
249+
}
250+
251+
// bytesReplace replace old bytes with given new.
252+
func bytesReplace(s, old, new []byte, n int) []byte {
253+
if n == 0 {
254+
return s
255+
}
256+
257+
if len(old) < len(new) {
258+
return bytes.Replace(s, old, new, n)
259+
}
260+
261+
if n < 0 {
262+
n = len(s)
263+
}
264+
265+
var wid, i, j, w int
266+
for i, j = 0, 0; i < len(s) && j < n; j++ {
267+
wid = bytes.Index(s[i:], old)
268+
if wid < 0 {
269+
break
270+
}
271+
272+
w += copy(s[w:], s[i:i+wid])
273+
w += copy(s[w:], new)
274+
i += wid + len(old)
275+
}
276+
277+
w += copy(s[w:], s[i:])
278+
return s[0:w]
279+
}
280+
243281
// genSheetPasswd provides a method to generate password for worksheet
244282
// protection by given plaintext. When an Excel sheet is being protected with
245283
// a password, a 16-bit (two byte) long hash is generated. To verify a

lib_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,8 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
203203
}
204204
}
205205
}
206+
207+
func TestBytesReplace(t *testing.T) {
208+
s := []byte{0x01}
209+
assert.EqualValues(t, s, bytesReplace(s, []byte{}, []byte{}, 0))
210+
}

picture.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
510510
err = nil
511511
for _, anchor := range deWsDr.TwoCellAnchor {
512512
deTwoCellAnchor = new(decodeTwoCellAnchor)
513-
if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>"))).
513+
if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>"))).
514514
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
515515
err = fmt.Errorf("xml decode error: %s", err)
516516
return

rows.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -424,14 +424,16 @@ func (f *File) RemoveRow(sheet string, row int) error {
424424
if row > len(xlsx.SheetData.Row) {
425425
return f.adjustHelper(sheet, rows, row, -1)
426426
}
427-
for rowIdx := range xlsx.SheetData.Row {
428-
if xlsx.SheetData.Row[rowIdx].R == row {
429-
xlsx.SheetData.Row = append(xlsx.SheetData.Row[:rowIdx],
430-
xlsx.SheetData.Row[rowIdx+1:]...)[:len(xlsx.SheetData.Row)-1]
431-
return f.adjustHelper(sheet, rows, row, -1)
427+
keep := 0
428+
for rowIdx := 0; rowIdx < len(xlsx.SheetData.Row); rowIdx++ {
429+
v := &xlsx.SheetData.Row[rowIdx]
430+
if v.R != row {
431+
xlsx.SheetData.Row[keep] = *v
432+
keep++
432433
}
433434
}
434-
return nil
435+
xlsx.SheetData.Row = xlsx.SheetData.Row[:keep]
436+
return f.adjustHelper(sheet, rows, row, -1)
435437
}
436438

437439
// InsertRow provides a function to insert a new row after given Excel row

sheet.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,9 @@ func (f *File) setAppXML() {
206206
// requirements about the structure of the input XML. This function is a
207207
// horrible hack to fix that after the XML marshalling is completed.
208208
func replaceRelationshipsBytes(content []byte) []byte {
209-
oldXmlns := []byte(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
210-
newXmlns := []byte("r")
211-
return bytes.Replace(content, oldXmlns, newXmlns, -1)
209+
oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
210+
newXmlns := stringToBytes("r")
211+
return bytesReplace(content, oldXmlns, newXmlns, -1)
212212
}
213213

214214
// SetActiveSheet provides function to set default active worksheet of XLSX by

sparkline.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup,
516516
for idx, ext = range decodeExtLst.Ext {
517517
if ext.URI == ExtURISparklineGroups {
518518
decodeSparklineGroups = new(decodeX14SparklineGroups)
519-
if err = f.xmlNewDecoder(bytes.NewReader([]byte(ext.Content))).
519+
if err = f.xmlNewDecoder(bytes.NewReader(stringToBytes(ext.Content))).
520520
Decode(decodeSparklineGroups); err != nil && err != io.EOF {
521521
return
522522
}

stream.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
365365
buf.WriteString(`>`)
366366
if c.V != "" {
367367
buf.WriteString(`<v>`)
368-
xml.EscapeText(buf, []byte(c.V))
368+
xml.EscapeText(buf, stringToBytes(c.V))
369369
buf.WriteString(`</v>`)
370370
}
371371
buf.WriteString(`</c>`)

0 commit comments

Comments
 (0)