diff --git a/excelizeam.go b/excelizeam.go index 721150e..94f6daa 100644 --- a/excelizeam.go +++ b/excelizeam.go @@ -10,11 +10,6 @@ import ( "github.com/xuri/excelize/v2" ) -const ( - DefaultRowBufferSize = 100000 - DefaultColBufferSize = 1000 -) - var ( ErrOverrideCellValue = errors.New("override cell value") ErrOverrideCellStyle = errors.New("override cell style") @@ -45,7 +40,7 @@ type excelizeam struct { maxCol int defaultBorder *DefaultBorders styleStore sync.Map - rowStore map[int]*StoredRow + rowStore sync.Map } type DefaultBorders struct { @@ -96,7 +91,7 @@ func New(sheetName string) (Excelizeam, error) { if err != nil { return nil, err } - return &excelizeam{sw: sw, rowStore: make(map[int]*StoredRow, DefaultRowBufferSize)}, nil + return &excelizeam{sw: sw}, nil } func (e *excelizeam) SetDefaultBorderStyle(style excelizestyle.BorderStyle, color excelizestyle.BorderColor) error { @@ -156,50 +151,53 @@ func (e *excelizeam) SetCellValue(colIndex, rowIndex int, value interface{}, sty if e.maxRow < rowIndex { e.maxRow = rowIndex } - if _, ok := e.rowStore[rowIndex]; !ok { - e.rowStore[rowIndex] = &StoredRow{ + if _, ok := e.rowStore.Load(rowIndex); !ok { + e.rowStore.Store(rowIndex, &StoredRow{ Row: Row{ Index: rowIndex, }, - } + }) } - if cachec, ok := e.rowStore[rowIndex].Cols.Load(colIndex); ok { - c := cachec.(*Cell) - if c.Value != nil && value != nil && !override { - return ErrOverrideCellValue - } - if value != nil { - c.Value = value - } + if cacherow, ok := e.rowStore.Load(rowIndex); ok { + r := cacherow.(*StoredRow) + if cachec, ok := r.Cols.Load(colIndex); ok { + c := cachec.(*Cell) + if c.Value != nil && value != nil && !override { + return ErrOverrideCellValue + } + if value != nil { + c.Value = value + } - if style != nil { - if c.StyleID > 0 { - if !override { - return ErrOverrideCellStyle - } - styleID, err := e.overrideStyle(c.StyleID, *style) - if err != nil { - return err - } - c.StyleID = styleID - } else { - styleID, err := e.getStyleID(style) - if err != nil { - return err + if style != nil { + if c.StyleID > 0 { + if !override { + return ErrOverrideCellStyle + } + styleID, err := e.overrideStyle(c.StyleID, *style) + if err != nil { + return err + } + c.StyleID = styleID + } else { + styleID, err := e.getStyleID(style) + if err != nil { + return err + } + c.StyleID = styleID } - c.StyleID = styleID } + return nil } - return nil - } - styleID, err := e.getStyleID(style) - if err != nil { - return err + styleID, err := e.getStyleID(style) + if err != nil { + return err + } + r.Cols.Store(colIndex, &Cell{ + StyleID: styleID, + Value: value, + }) } - e.rowStore[rowIndex].Cols.Store(colIndex, &Cell{ - StyleID: styleID, - Value: value, - }) return nil } @@ -211,41 +209,44 @@ func (e *excelizeam) SetStyleCell(colIndex, rowIndex int, style excelize.Style, e.maxRow = rowIndex } - if _, ok := e.rowStore[rowIndex]; !ok { - e.rowStore[rowIndex] = &StoredRow{ + if _, ok := e.rowStore.Load(rowIndex); !ok { + e.rowStore.Store(rowIndex, &StoredRow{ Row: Row{ Index: rowIndex, }, - } + }) } - if cachec, ok := e.rowStore[rowIndex].Cols.Load(colIndex); ok { - c := cachec.(*Cell) - if c.StyleID > 0 { - if !override { - return ErrOverrideCellStyle - } - styleID, err := e.overrideStyle(c.StyleID, style) - if err != nil { - return err - } - c.StyleID = styleID - } else { - styleID, err := e.getStyleID(&style) - if err != nil { - return err + if cacherow, ok := e.rowStore.Load(rowIndex); ok { + r := cacherow.(*StoredRow) + if cachec, ok := r.Cols.Load(colIndex); ok { + c := cachec.(*Cell) + if c.StyleID > 0 { + if !override { + return ErrOverrideCellStyle + } + styleID, err := e.overrideStyle(c.StyleID, style) + if err != nil { + return err + } + c.StyleID = styleID + } else { + styleID, err := e.getStyleID(&style) + if err != nil { + return err + } + c.StyleID = styleID } - c.StyleID = styleID + return nil } - return nil - } - styleID, err := e.getStyleID(&style) - if err != nil { - return err + styleID, err := e.getStyleID(&style) + if err != nil { + return err + } + r.Cols.Store(colIndex, &Cell{ + StyleID: styleID, + Value: nil, + }) } - e.rowStore[rowIndex].Cols.Store(colIndex, &Cell{ - StyleID: styleID, - Value: nil, - }) return nil } @@ -259,41 +260,44 @@ func (e *excelizeam) SetStyleCellRange(startColIndex, startRowIndex, endColIndex for rowIdx := startRowIndex; rowIdx <= endRowIndex; rowIdx++ { for colIdx := startColIndex; colIdx <= endColIndex; colIdx++ { - if _, ok := e.rowStore[rowIdx]; !ok { - e.rowStore[rowIdx] = &StoredRow{ + if _, ok := e.rowStore.Load(rowIdx); !ok { + e.rowStore.Store(rowIdx, &StoredRow{ Row: Row{ Index: rowIdx, }, - } + }) } - if cachec, ok := e.rowStore[rowIdx].Cols.Load(colIdx); ok { - c := cachec.(*Cell) - if c.StyleID > 0 { - if !override { - return ErrOverrideCellStyle - } - styleID, err := e.overrideStyle(c.StyleID, style) - if err != nil { - return err - } - c.StyleID = styleID - } else { - styleID, err := e.getStyleID(&style) - if err != nil { - return err + if cacherow, ok := e.rowStore.Load(rowIdx); ok { + r := cacherow.(*StoredRow) + if cachec, ok := r.Cols.Load(colIdx); ok { + c := cachec.(*Cell) + if c.StyleID > 0 { + if !override { + return ErrOverrideCellStyle + } + styleID, err := e.overrideStyle(c.StyleID, style) + if err != nil { + return err + } + c.StyleID = styleID + } else { + styleID, err := e.getStyleID(&style) + if err != nil { + return err + } + c.StyleID = styleID } - c.StyleID = styleID + return nil } - return nil - } - styleID, err := e.getStyleID(&style) - if err != nil { - return err + styleID, err := e.getStyleID(&style) + if err != nil { + return err + } + r.Cols.Store(colIdx, &Cell{ + StyleID: styleID, + Value: nil, + }) } - e.rowStore[rowIdx].Cols.Store(colIdx, &Cell{ - StyleID: styleID, - Value: nil, - }) } } return nil @@ -468,41 +472,44 @@ func (e *excelizeam) SetBorderRange(startColIndex, startRowIndex, endColIndex, e } style := excelize.Style{Border: borderStyles} - if _, ok := e.rowStore[rowIdx]; !ok { - e.rowStore[rowIdx] = &StoredRow{ + if _, ok := e.rowStore.Load(rowIdx); !ok { + e.rowStore.Store(rowIdx, &StoredRow{ Row: Row{ Index: rowIdx, }, - } + }) } - if cachec, ok := e.rowStore[rowIdx].Cols.Load(colIdx); ok { - c := cachec.(*Cell) - if c.StyleID > 0 { - if !override { - return ErrOverrideCellStyle - } - styleID, err := e.overrideStyle(c.StyleID, style) - if err != nil { - return err + if cacherow, ok := e.rowStore.Load(rowIdx); ok { + r := cacherow.(*StoredRow) + if cachec, ok := r.Cols.Load(colIdx); ok { + c := cachec.(*Cell) + if c.StyleID > 0 { + if !override { + return ErrOverrideCellStyle + } + styleID, err := e.overrideStyle(c.StyleID, style) + if err != nil { + return err + } + c.StyleID = styleID + } else { + styleID, err := e.getStyleID(&style) + if err != nil { + return err + } + c.StyleID = styleID } - c.StyleID = styleID - } else { - styleID, err := e.getStyleID(&style) - if err != nil { - return err - } - c.StyleID = styleID + continue } - continue - } - styleID, err := e.getStyleID(&style) - if err != nil { - return err + styleID, err := e.getStyleID(&style) + if err != nil { + return err + } + r.Cols.Store(colIdx, &Cell{ + StyleID: styleID, + Value: nil, + }) } - e.rowStore[rowIdx].Cols.Store(colIdx, &Cell{ - StyleID: styleID, - Value: nil, - }) } } return nil @@ -654,7 +661,7 @@ func (e *excelizeam) writeStream() error { } for rowIdx := 1; rowIdx <= e.maxRow; rowIdx++ { - r, rowOK := e.rowStore[rowIdx] + cacherow, rowOK := e.rowStore.Load(rowIdx) if !rowOK { // Value/Styleのない行はデフォルトStyleのみ設定 if e.defaultBorder != nil { @@ -672,6 +679,7 @@ func (e *excelizeam) writeStream() error { continue } + r := cacherow.(*StoredRow) canWrite := false cellValues := make([]interface{}, e.maxCol) if e.defaultBorder != nil { diff --git a/excelizeam_test.go b/excelizeam_test.go index d1f6086..971b1a1 100644 --- a/excelizeam_test.go +++ b/excelizeam_test.go @@ -321,6 +321,17 @@ func BenchmarkExcelizeam(b *testing.B) { } } }) + b.Run("Excelizeam Async", func(b *testing.B) { + var buf bytes.Buffer + defer buf.Reset() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := benchExcelizeamAsync(&buf); err != nil { + b.Error(err) + } + } + }) } func benchExcelize(w io.Writer) error { @@ -359,7 +370,9 @@ func benchExcelizeAsync(w io.Writer) error { var eg errgroup.Group for rowIdx := 1; rowIdx <= 1000; rowIdx++ { + rowIdx := rowIdx for colIdx := 1; colIdx <= 10; colIdx++ { + colIdx := colIdx eg.Go(func() error { cell, err := excelize.CoordinatesToCellName(colIdx, rowIdx) if err != nil { @@ -445,6 +458,37 @@ func benchExcelizeam(w io.Writer) error { return e.Write(w) } +func benchExcelizeamAsync(w io.Writer) error { + e, err := excelizeam.New("test") + if err != nil { + return err + } + + var eg errgroup.Group + + for rowIdx := 1; rowIdx <= 1000; rowIdx++ { + rowIdx := rowIdx + for colIdx := 1; colIdx <= 10; colIdx++ { + colIdx := colIdx + eg.Go(func() error { + if err := e.SetCellValue(colIdx, rowIdx, fmt.Sprintf("test%d-%d", rowIdx, colIdx), &excelize.Style{ + Border: excelizestyle.BorderAround(excelizestyle.BorderStyleContinuous2, excelizestyle.BorderColorBlack), + Font: &excelize.Font{Size: 12, Bold: true}, + Alignment: excelizestyle.Alignment(excelizestyle.AlignmentHorizontalCenter, excelizestyle.AlignmentVerticalCenter, true), + }, false); err != nil { + return err + } + return nil + }) + } + } + + if err := eg.Wait(); err != nil { + return err + } + return e.Write(w) +} + func Assert(t *testing.T, expected, actual *excelize.File) { for rowIdx := 1; rowIdx <= 10; rowIdx++ { for colIdx := 1; colIdx <= 10; colIdx++ {