From 63283c100ac8355881772d4ad30903d883aa5d3b Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 9 Jul 2014 14:56:09 +0000 Subject: [PATCH] fpspreadsheet: Caching the result of the worksheet's GetLastColIndex and GetLastRowIndex for better writing performance of large ods files (which query these data in the row loop). Has no significant effect, though. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3302 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpsopendocument.pas | 6 +- components/fpspreadsheet/fpspreadsheet.pas | 118 +++++++++++++------ 2 files changed, 89 insertions(+), 35 deletions(-) diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index 161239838..8c56e7bd2 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -2782,10 +2782,12 @@ begin // Look for empty rows with the same style, they need the "number-rows-repeated" element. rowsRepeated := 1; - if ASheet.GetCellCountInRow(r) = 0 then begin + if (ASheet.GetFirstCellOfRow(r) = nil) then begin +// if ASheet.GetCellCountInRow(r) = 0 then begin rr := r + 1; while (rr <= lastRow) do begin - if ASheet.GetCellCountInRow(rr) > 0 then begin + if ASheet.GetFirstCellOfRow(rr) = nil then begin +// if ASheet.GetCellCountInRow(rr) > 0 then begin break; end; h1 := ASheet.GetRowHeight(rr); diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index f9dd329aa..cabae6a52 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -479,6 +479,8 @@ type FLeftPaneWidth: Integer; FTopPaneHeight: Integer; FOptions: TsSheetOptions; + FLastRowIndex: Cardinal; + FLastColIndex: Cardinal; FOnChangeCell: TsCellEvent; FOnChangeFont: TsCellEvent; @@ -508,6 +510,7 @@ type { Utils } class function CellPosToText(ARow, ACol: Cardinal): string; procedure RemoveAllCells; + procedure UpdateCaches; { Reading of values } function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload; @@ -636,9 +639,9 @@ type function GetNextCell(): PCell; function GetFirstCellOfRow(ARow: Cardinal): PCell; function GetLastCellOfRow(ARow: Cardinal): PCell; - function GetLastColIndex: Cardinal; + function GetLastColIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastColNumber: Cardinal; deprecated 'Use GetLastColIndex'; - function GetLastRowIndex: Cardinal; + function GetLastRowIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastRowNumber: Cardinal; deprecated 'Use GetLastRowIndex'; { Data manipulation methods - For Rows and Cols } @@ -708,6 +711,7 @@ type { Internal methods } procedure PrepareBeforeSaving; procedure RemoveWorksheetsCallback(data, arg: pointer); + procedure UpdateCaches; public {@@ A copy of SysUtil's DefaultFormatSettings to provide some kind of @@ -1414,6 +1418,9 @@ begin FRows := TIndexedAVLTree.Create(@CompareRows); FCols := TIndexedAVLTree.Create(@CompareCols); + FLastRowIndex := 0; + FLastColIndex := 0; + FOptions := [soShowGridLines, soShowHeaders]; end; @@ -1761,6 +1768,10 @@ begin Result^.BorderStyles := DEFAULT_BORDERSTYLES; Cells.Add(Result); + if FLastColIndex = 0 then FLastColIndex := GetLastColIndex(true) + else FLastColIndex := Max(FLastColIndex, ACol); + if FLastRowIndex = 0 then FLastRowIndex := GetLastRowIndex(true) + else FLastRowIndex := Max(FLastRowIndex, ARow); end; end; @@ -1897,30 +1908,38 @@ end; Use GetCellCount to verify if there is at least one cell with contents in the worksheet. + @param AForceCalculation The index of the last column is continuously updated + whenever a new cell is created. If AForceCalculation + is true all cells are scanned to determine the index + of the last column. @see GetCellCount } -function TsWorksheet.GetLastColIndex: Cardinal; +function TsWorksheet.GetLastColIndex(AForceCalculation: Boolean = false): Cardinal; var AVLNode: TAVLTreeNode; i: Integer; begin - Result := 0; - - // Traverse the tree from lowest to highest. - // Since tree primary sort order is on Row - // highest Col could exist anywhere. - AVLNode := FCells.FindLowest; - While Assigned(AVLNode) do - begin - Result := Math.Max(Result, PCell(AVLNode.Data)^.Col); - AVLNode := FCells.FindSuccessor(AVLNode); - end; - - // In addition, there may be column records defining the column width even - // without content - for i:=0 to FCols.Count-1 do - if FCols[i] <> nil then - Result := Math.Max(Result, PCol(FCols[i])^.Col); + if AForceCalculation then begin + Result := 0; + // Traverse the tree from lowest to highest. + // Since tree primary sort order is on Row + // highest Col could exist anywhere. + AVLNode := FCells.FindLowest; + While Assigned(AVLNode) do + begin + Result := Math.Max(Result, PCell(AVLNode.Data)^.Col); + AVLNode := FCells.FindSuccessor(AVLNode); + end; + // In addition, there may be column records defining the column width even + // without content + for i:=0 to FCols.Count-1 do + if FCols[i] <> nil then + Result := Math.Max(Result, PCol(FCols[i])^.Col); + // Store the result + FLastColIndex := Result; + end + else + Result := FLastColIndex; end; {@@ @@ -1979,23 +1998,31 @@ end; Use GetCellCount to verify if there is at least one cell with contents in the worksheet. + @param AForceCalculation The index of the last row is continuously updated + whenever a new cell is created. If AForceCalculation + is true all cells are scanned to determine the index + of the last row. @see GetCellCount } -function TsWorksheet.GetLastRowIndex: Cardinal; +function TsWorksheet.GetLastRowIndex(AForceCalculation: Boolean = false): Cardinal; var AVLNode: TAVLTreeNode; i: Integer; begin - Result := 0; - - AVLNode := FCells.FindHighest; - if Assigned(AVLNode) then - Result := PCell(AVLNode.Data).Row; - - // In addition, there may be row records even for empty rows. - for i:=0 to FRows.Count-1 do - if FRows[i] <> nil then - Result := Math.Max(Result, PRow(FRows[i])^.Row); + if AForceCalculation then begin + Result := 0; + AVLNode := FCells.FindHighest; + if Assigned(AVLNode) then + Result := PCell(AVLNode.Data).Row; + // In addition, there may be row records even for empty rows. + for i:=0 to FRows.Count-1 do + if FRows[i] <> nil then + Result := Math.Max(Result, PRow(FRows[i])^.Row); + // Store result + FLastRowIndex := Result; + end + else + Result := FLastRowIndex end; {@@ @@ -2295,7 +2322,7 @@ begin // Now we construct the string from the parts stored in the stringlist. // Every item processed is deleted from the list for error detection. - // In order not to confuse mix up we start at the end of the list and + // In order not to confuse indexes we start at the end of the list and // work forward. i := L.Count-1; while (L.Count > 0) and (i >= 0) do begin @@ -2433,6 +2460,15 @@ begin FCells.Clear; end; +{@@ + Helper method to update internal caching variables +} +procedure TsWorksheet.UpdateCaches; +begin + FLastColIndex := GetLastColIndex(true); + FLastRowIndex := GetLastRowIndex(true); +end; + {@@ Writes UTF-8 encoded text to a cell. @@ -3656,6 +3692,8 @@ begin FillChar(Result^, SizeOf(TRow), #0); Result^.Row := ARow; FRows.Add(Result); + if FLastRowIndex = 0 then FLastRowIndex := GetLastRowIndex(true) + else FLastRowIndex := Max(FLastRowIndex, ARow); end; end; @@ -3674,6 +3712,8 @@ begin FillChar(Result^, SizeOf(TCol), #0); Result^.Col := ACol; FCols.Add(Result); + if FLastColIndex = 0 then FLastColIndex := GetLastColIndex(true) + else FLastColIndex := Max(FLastColIndex, ACol); end; end; @@ -3887,6 +3927,17 @@ begin TsWorksheet(data).Free; end; +{@@ + Helper method to update internal caching variables +} +procedure TsWorkbook.UpdateCaches; +var + sheet: TsWorksheet; +begin + for sheet in FWorksheets do + sheet.UpdateCaches; +end; + {@@ Constructor of the workbook class. Among others, it initializes the built-in fonts, defines the default font, and sets up the FormatSettings for localization @@ -4012,6 +4063,7 @@ begin try FFileName := AFileName; AReader.ReadFromFile(AFileName, Self); + UpdateCaches; FFormat := AFormat; finally AReader.Free; @@ -4096,9 +4148,9 @@ var AReader: TsCustomSpreadReader; begin AReader := CreateSpreadReader(AFormat); - try AReader.ReadFromStream(AStream, Self); + UpdateCaches; finally AReader.Free; end;