From d93e470a52b87661ef214fbb593b0b7696d72b6a Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 28 Jul 2018 14:54:49 +1200 Subject: [PATCH 01/18] Suppress GCC maybe-uninitialized warning Signal to noise ratio is very high, and the #pragma suppressions for optional aren't working for GCC8 --- source/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 5c924e84..186e57fc 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -33,6 +33,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # all warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") # extra warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # ignore MSVC and Clang pragmas + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized") # GCC diagnostic with lots of false positives elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything") # all warnings # blacklist warnings that are not relevant From ecb71db558b6a3c983b0579ce68d2001fc0a62f3 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 28 Jul 2018 15:38:36 +1200 Subject: [PATCH 02/18] Run samples and benchmarks on CI --- .travis.yml | 12 ++ samples/data/documentation-print.xlsx | Bin 0 -> 8831 bytes samples/documentation.cpp | 226 ++++++++++++++++++++++++++ samples/sample.cpp | 41 ----- 4 files changed, 238 insertions(+), 41 deletions(-) create mode 100644 samples/data/documentation-print.xlsx create mode 100644 samples/documentation.cpp delete mode 100644 samples/sample.cpp diff --git a/.travis.yml b/.travis.yml index 0e25f336..1aa1429d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -170,6 +170,18 @@ script: - cmake .. -DXLNT_CXX_LANG=${CXX_VER} -DSTATIC=$STATIC -DBENCHMARKS=$BENCHMARKS -DSAMPLES=$SAMPLES -DCOVERAGE=$COVERAGE -DCMAKE_BUILD_TYPE=$BUILD_TYPE - cmake --build . -- -j2 - ./tests/xlnt.test + - | + if [[ "${SAMPLES}" == "ON" ]]; then + ./samples/sample-decrypt + ./samples/sample-img2xlsx + ./samples/sample-documentation + fi + - | + if [[ "${BENCHMARKS}" == "ON" ]]; then + ./benchmarks/benchmark-styles + ./benchmarks/benchmark-writer + fi + after_success: - | diff --git a/samples/data/documentation-print.xlsx b/samples/data/documentation-print.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..48fc67fcacb4c33ac1d9ad17076553847db37ff6 GIT binary patch literal 8831 zcmeHMWmr`E_8vgGk#gupkRAjnNhPI`RJyxGy1Sbp1`rS(LQ2{pL=cdUK|&fq1|%i_ zqvxKhhvR*o`|1DXo;}aA_lLdLyZ8Kl>s{+zYpE%rT_Xix0I&c600Y3+K9k%Y1pp{O z2LP@Euuu(ToE+UP9NiylJ#n^hGvfAku%|D)hRR+5Kt(?P&+$LJ0{y8*N}ar<;Ry6g znO-PpnlV}#h8q{V547xf@5*gOkI_08$DSK!Y({Hw`t>47g!sAj?Qk(Vs8^uPCNhFfYc2tEc zE+d&nE(saM0|J;8R^Oo*8&bUysPts=>2FL&8MDJnCAGplF87NJ*&b#VPDo`(c`zSl zWL`Vl$i{%B-YEfU;+~&N8d-PmbZVf3w`dlKw8-^M4znTs>+%u}p!OG%)@k!H9U$*0 zBRPnRB7=bUG+&3Xz?9L- zhO`G5Pi^vWcFz8>cX4Wt>EvnItC$#IQ|cL$DTqdu6nA&a^;r_HdY_5)aLSD`i;t-* z;^FPIYFuLDFk39lrGQQ7LLjeqm2+KH5!kb2ErL-}?D+>!8?TY3L^v^J5cO zH=)+!-?C&5`=MSQIIl0=IIyu>WPiB9T1_hbXkk9^aA1x^# zY=1aSNNdQjpG&f%MXE?^tW>4J5r9>Faj5Hd`XQUs-4vN7$zE;*eSKf4?bpVfX?`NCc0B(3ZnZWN8Q)%Eo8xJ%sxf!1p?ELaJ{ezH%!Ara5CPyV- zmldmo^h(`#P85UYM0cV;57BdG>NEHBvPN~$S9UH7h4*F{zoP0iSK^%!GU?LC)+o8uP;0GZAc^uZ(~~Dmc~h&e^ZXeVhFE>wH^I4wjKAZ*|}T5 z%|p70ZEOl5_+xdU3OrNBD>57Jhvk{ z0s~Tx2+SNh4&G17i;^*cU;Uojs@BLsK=y<2k!70{>X7ZC5?Ne2+GjZQnj#&;w|AD_ z(0o1(8F`({qAsmBk>l$jE6qS4@^W}CjAn09X0V6rRh(&2!R|a=9n&4hw4{244d>|L zJZ?>JL12l2LmHli;x}{t%@27}U-|;i6sYfWi8n7INV=XjDJokbpellIOKmrCXp9l7 zI8+VEF7J162P%iAj;t@(L>i^d=8Kv$b??U~yF%Rh%eitRKUw!T&rGj)^|m3``tMoq z)byD{Dh2>hO7&fu{@4w7YYPVpo*!qv?`+=EQ+1lTP1;U4h3OGJRh zq3UvsptZ?L*VQbP98vJ2f-eQ4bT*k}pfrEswdWi{ZFbLRHHd(sosb>ME54$9uQY-| zH8CC`%NXE$Ilsi@aR3jAgvc;5uei*-Fsks0?2@1x6}Eor6@IQnMVpM%W7hAj44(tb zBj6LK2&3*C4rXGhDv9)**I&XES#r{%_f06@zZYZR>sNUlSrz<{SnDk+9m;H^_C)!( zB})hBP%V~;{^?VGh=i1k%E(M#YI$bozzPfLlW6XN$TJ=!1 z1#lI64rddEO}7Q8IjIvf*^AeXQxi$PY^#5s@<=!dR+3O(r<1iBPRPOjXfmxF9;dW9_qjFLMYxKdTz7jUyS-y0)TOQwPBSEY z%k*xdCYE~aVIxNf!bM1CU-THIhjZ31gQRD+lpoj69N7&}wU&#+Dq}yK% z%^5s~JY4zOJ-H%xGn@lTMKWWTGbpM*?XAjgh9+pWTmous2*tPl7$MapT|3EjuJ?qFM2`xLMg^hSg{61Sf#B zUtLBHS)H}Qd)QGbm1dPq$=~>;REFZ8YNkw^f9uGBD)R|p+d7|`d}%AL(!?ZT7$-=F z;NuIVg#%&)**Gut(fWPVaZ;>t8_^l27*;)eO48tTgWN(rR)g0! z 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 Cat9s& literal 0 HcmV?d00001 diff --git a/samples/documentation.cpp b/samples/documentation.cpp new file mode 100644 index 00000000..067300bf --- /dev/null +++ b/samples/documentation.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2017-2018 Thomas Fussell +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE +// +// @license: http://www.opensource.org/licenses/mit-license.php +// @author: see AUTHORS file + +#include +#include "helpers/path_helper.hpp" + +// Readme +// from https://tfussell.gitbooks.io/xlnt/content/ and https://github.com/tfussell/xlnt/blob/master/README.md +void sample_readme_example1() +{ + xlnt::workbook wb; + xlnt::worksheet ws = wb.active_sheet(); + + ws.cell("A1").value(5); + ws.cell("B2").value("string data"); + ws.cell("C3").formula("=RAND()"); + + ws.merge_cells("C3:C4"); + ws.freeze_panes("B2"); + + wb.save("sample.xlsx"); +} + +// Simple - reading from an existing xlsx spread sheet. +// from https://tfussell.gitbooks.io/xlnt/content/docs/introduction/Examples.html +void sample_read_and_print_example() +{ + xlnt::workbook wb; + wb.load(path_helper::sample_file("documentation-print.xlsx")); // modified to use the test data directory + auto ws = wb.active_sheet(); + std::clog << "Processing spread sheet" << std::endl; + for (auto row : ws.rows(false)) + { + for (auto cell : row) + { + std::clog << cell.to_string() << std::endl; + } + } + std::clog << "Processing complete" << std::endl; +} + +// Simple - storing a spread sheet in a 2 dimensional C++ Vector for further processing +// from https://tfussell.gitbooks.io/xlnt/content/docs/introduction/Examples.html +void sample_read_into_vector_example() +{ + xlnt::workbook wb; + wb.load(path_helper::sample_file("documentation-print.xlsx")); // modified to use the test data directory + auto ws = wb.active_sheet(); + std::clog << "Processing spread sheet" << std::endl; + std::clog << "Creating a single vector which stores the whole spread sheet" << std::endl; + std::vector> theWholeSpreadSheet; + for (auto row : ws.rows(false)) + { + std::clog << "Creating a fresh vector for just this row in the spread sheet" << std::endl; + std::vector aSingleRow; + for (auto cell : row) + { + std::clog << "Adding this cell to the row" << std::endl; + aSingleRow.push_back(cell.to_string()); + } + std::clog << "Adding this entire row to the vector which stores the whole spread sheet" << std::endl; + theWholeSpreadSheet.push_back(aSingleRow); + } + std::clog << "Processing complete" << std::endl; + std::clog << "Reading the vector and printing output to the screen" << std::endl; + for (int rowInt = 0; rowInt < theWholeSpreadSheet.size(); rowInt++) + { + for (int colInt = 0; colInt < theWholeSpreadSheet.at(rowInt).size(); colInt++) + { + std::cout << theWholeSpreadSheet.at(rowInt).at(colInt) << std::endl; + } + } +} + +// Simple - writing values to a new xlsx spread sheet. +// from https://tfussell.gitbooks.io/xlnt/content/docs/introduction/Examples.html +void sample_write_sheet_to_file_example() +{ + // Creating a 2 dimensional vector which we will write values to + std::vector> wholeWorksheet; + //Looping through each row (100 rows as per the second argument in the for loop) + for (int outer = 0; outer < 100; outer++) + { + //Creating a fresh vector for a fresh row + std::vector singleRow; + //Looping through each of the columns (100 as per the second argument in the for loop) in this particular row + for (int inner = 0; inner < 100; inner++) + { + //Adding a single value in each cell of the row + std::string val = std::to_string(inner + 1); + singleRow.push_back(val); + } + //Adding the single row to the 2 dimensional vector + wholeWorksheet.push_back(singleRow); + std::clog << "Writing to row " << outer << " in the vector " << std::endl; + } + //Writing to the spread sheet + //Creating the output workbook + std::clog << "Creating workbook" << std::endl; + xlnt::workbook wbOut; + //Setting the destination output file name + std::string dest_filename = "output.xlsx"; + //Creating the output worksheet + xlnt::worksheet wsOut = wbOut.active_sheet(); + //Giving the output worksheet a title/name + wsOut.title("data"); + //We will now be looping through the 2 dimensional vector which we created above + //In this case we have two iterators one for the outer loop (row) and one for the inner loop (column) + std::clog << "Looping through vector and writing to spread sheet" << std::endl; + for (int fOut = 0; fOut < wholeWorksheet.size(); fOut++) + { + std::clog << "Row" << fOut << std::endl; + for (int fIn = 0; fIn < wholeWorksheet.at(fOut).size(); fIn++) + { + //Take notice of the difference between accessing the vector and accessing the work sheet + //As you may already know Excel spread sheets start at row 1 and column 1 (not row 0 and column 0 like you would expect from a C++ vector) + //In short the xlnt cell reference starts at column 1 row 1 (hence the + 1s below) and the vector reference starts at row 0 and column 0 + wsOut.cell(xlnt::cell_reference(fIn + 1, fOut + 1)).value(wholeWorksheet.at(fOut).at(fIn)); + //Further clarification to avoid confusion + //Cell reference arguments are (column number, row number); e.g. cell_reference(fIn + 1, fOut + 1) + //Vector arguments are (row number, column number); e.g. wholeWorksheet.at(fOut).at(fIn) + } + } + std::clog << "Finished writing spread sheet" << std::endl; + wbOut.save(dest_filename); +} + +// Number Formatting +// from https://tfussell.gitbooks.io/xlnt/content/docs/advanced/Formatting.html +void sample_number_formatting_example() +{ + xlnt::workbook wb; + auto cell = wb.active_sheet().cell("A1"); + cell.number_format(xlnt::number_format::percentage()); + cell.value(0.513); + std::cout << '\n' + << cell.to_string() << std::endl; +} +// Properties +// from https://tfussell.gitbooks.io/xlnt/content/docs/advanced/Properties.html +void sample_properties_example() +{ + xlnt::workbook wb; + wb.core_property(xlnt::core_property::category, "hors categorie"); + wb.core_property(xlnt::core_property::content_status, "good"); + wb.core_property(xlnt::core_property::created, xlnt::datetime(2017, 1, 15)); + wb.core_property(xlnt::core_property::creator, "me"); + wb.core_property(xlnt::core_property::description, "description"); + wb.core_property(xlnt::core_property::identifier, "id"); + wb.core_property(xlnt::core_property::keywords, {"wow", "such"}); + wb.core_property(xlnt::core_property::language, "Esperanto"); + wb.core_property(xlnt::core_property::last_modified_by, "someone"); + wb.core_property(xlnt::core_property::last_printed, xlnt::datetime(2017, 1, 15)); + wb.core_property(xlnt::core_property::modified, xlnt::datetime(2017, 1, 15)); + wb.core_property(xlnt::core_property::revision, "3"); + wb.core_property(xlnt::core_property::subject, "subject"); + wb.core_property(xlnt::core_property::title, "title"); + wb.core_property(xlnt::core_property::version, "1.0"); + wb.extended_property(xlnt::extended_property::application, "xlnt"); + wb.extended_property(xlnt::extended_property::app_version, "0.9.3"); + wb.extended_property(xlnt::extended_property::characters, 123); + wb.extended_property(xlnt::extended_property::characters_with_spaces, 124); + wb.extended_property(xlnt::extended_property::company, "Incorporated Inc."); + wb.extended_property(xlnt::extended_property::dig_sig, "?"); + wb.extended_property(xlnt::extended_property::doc_security, 0); + wb.extended_property(xlnt::extended_property::heading_pairs, true); + wb.extended_property(xlnt::extended_property::hidden_slides, false); + wb.extended_property(xlnt::extended_property::h_links, 0); + wb.extended_property(xlnt::extended_property::hyperlink_base, 0); + wb.extended_property(xlnt::extended_property::hyperlinks_changed, true); + wb.extended_property(xlnt::extended_property::lines, 42); + wb.extended_property(xlnt::extended_property::links_up_to_date, false); + wb.extended_property(xlnt::extended_property::manager, "johnny"); + wb.extended_property(xlnt::extended_property::m_m_clips, "?"); + wb.extended_property(xlnt::extended_property::notes, "note"); + wb.extended_property(xlnt::extended_property::pages, 19); + wb.extended_property(xlnt::extended_property::paragraphs, 18); + wb.extended_property(xlnt::extended_property::presentation_format, "format"); + wb.extended_property(xlnt::extended_property::scale_crop, true); + wb.extended_property(xlnt::extended_property::shared_doc, false); + wb.extended_property(xlnt::extended_property::slides, 17); + wb.extended_property(xlnt::extended_property::template_, "template!"); + wb.extended_property(xlnt::extended_property::titles_of_parts, {"title"}); + wb.extended_property(xlnt::extended_property::total_time, 16); + wb.extended_property(xlnt::extended_property::words, 101); + wb.custom_property("test", {1, 2, 3}); + wb.custom_property("Editor", "John Smith"); + wb.save("lots_of_properties.xlsx"); +} + +int main() +{ + sample_readme_example1(); + std::clog << '\n'; + sample_read_and_print_example(); + std::clog << '\n'; + sample_read_into_vector_example(); + std::clog << '\n'; + sample_write_sheet_to_file_example(); + std::clog << '\n'; + sample_number_formatting_example(); + std::clog << '\n'; + sample_properties_example(); + std::clog.flush(); + return 0; +} \ No newline at end of file diff --git a/samples/sample.cpp b/samples/sample.cpp deleted file mode 100644 index 7f1a89b0..00000000 --- a/samples/sample.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2017-2018 Thomas Fussell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE -// -// @license: http://www.opensource.org/licenses/mit-license.php -// @author: see AUTHORS file - -#include - -int main() -{ - xlnt::workbook wb; - xlnt::worksheet ws = wb.active_sheet(); - - ws.cell("A1").value(5); - ws.cell("B2").value("string data"); - ws.cell("C3").formula("=RAND()"); - - ws.merge_cells("C3:C4"); - ws.freeze_panes("B2"); - - wb.save("sample.xlsx"); - - return 0; -} From 6b427127ff2cf019b5fdb37c661cf9ef51bc9a26 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 28 Jul 2018 15:57:18 +1200 Subject: [PATCH 03/18] Add folds to travis log, parameters for img sample --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1aa1429d..695daf88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -172,14 +172,18 @@ script: - ./tests/xlnt.test - | if [[ "${SAMPLES}" == "ON" ]]; then + echo 'travis_fold:start:samples' ./samples/sample-decrypt - ./samples/sample-img2xlsx + ./samples/sample-img2xlsx "../samples/data/cafe.jpg" "img.xlsx" ./samples/sample-documentation + echo 'travis_fold:end:samples' fi - | if [[ "${BENCHMARKS}" == "ON" ]]; then + echo 'travis_fold:start:bencmarks' ./benchmarks/benchmark-styles ./benchmarks/benchmark-writer + echo 'travis_fold:end:bencmarks' fi From 0fe0c20d2a3d8bca12b1062d1326206a749ca28b Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 28 Jul 2018 16:07:39 +1200 Subject: [PATCH 04/18] Cut down on builds executing samples/benchmarks, formatting --- .travis.yml | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/.travis.yml b/.travis.yml index 695daf88..43d6dec7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ matrix: - COVERAGE=OFF - STATIC=OFF - SAMPLES=OFF + - BENCHMARKS=OFF # gcc-7, c++14, release build, static linking, samples + benchmarks compiled - os: linux @@ -47,7 +48,8 @@ matrix: - BUILD_TYPE=Release - COVERAGE=OFF - STATIC=ON - - SAMPLES=ON + - SAMPLES=OFF + - BENCHMARKS=OFF # gcc-8, c++17, release build, static linking, samples + benchmarks compiled - os: linux @@ -66,6 +68,7 @@ matrix: - COVERAGE=OFF - STATIC=ON - SAMPLES=ON + - BENCHMARKS=ON # =========== CLANG ============= # clang 4, c++11, release build, dynamic linking @@ -86,6 +89,7 @@ matrix: - COVERAGE=OFF - STATIC=OFF - SAMPLES=OFF + - BENCHMARKS=OFF # clang 5, c++14, release build, dynamic linking, samples + benchmarks compiled - os: linux @@ -104,7 +108,8 @@ matrix: - BUILD_TYPE=Release - COVERAGE=OFF - STATIC=ON - - SAMPLES=ON + - SAMPLES=OFF + - BENCHMARKS=OFF # clang 6, c++17, release build, static linking, samples compiled - os: linux @@ -124,6 +129,7 @@ matrix: - COVERAGE=OFF - STATIC=ON - SAMPLES=ON + - BENCHMARKS=ON # ============= CODE COVERAGE =============== # gcc-6, c++11, debug build, static linking, code coverage enabled @@ -159,32 +165,20 @@ install: - cmake --version script: - - | - if [[ "${BUILD_TYPE}" == "Release" && "${STATIC}" == "ON" ]]; - then BENCHMARKS="ON"; - else BENCHMARKS="OFF"; - fi - - mkdir build - cd build - cmake .. -DXLNT_CXX_LANG=${CXX_VER} -DSTATIC=$STATIC -DBENCHMARKS=$BENCHMARKS -DSAMPLES=$SAMPLES -DCOVERAGE=$COVERAGE -DCMAKE_BUILD_TYPE=$BUILD_TYPE - cmake --build . -- -j2 - ./tests/xlnt.test - - | - if [[ "${SAMPLES}" == "ON" ]]; then - echo 'travis_fold:start:samples' - ./samples/sample-decrypt - ./samples/sample-img2xlsx "../samples/data/cafe.jpg" "img.xlsx" - ./samples/sample-documentation - echo 'travis_fold:end:samples' - fi - - | - if [[ "${BENCHMARKS}" == "ON" ]]; then - echo 'travis_fold:start:bencmarks' - ./benchmarks/benchmark-styles - ./benchmarks/benchmark-writer - echo 'travis_fold:end:bencmarks' - fi + - echo 'travis_fold:start:samples' + - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-decrypt; fi + - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-img2xlsx "../samples/data/cafe.jpg" "img.xlsx"; fi + - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-documentation; fi + - echo 'travis_fold:end:samples' + - echo 'travis_fold:start:benchmarks' + - if [[ "${BENCHMARKS}" == "ON" ]]; then ./benchmarks/benchmark-styles; fi + - if [[ "${BENCHMARKS}" == "ON" ]]; then ./benchmarks/benchmark-writer; fi + - echo 'travis_fold:end:benchmarks' after_success: From e0d5cdbd8503829636dc559e35a8a1de13455aa0 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 28 Jul 2018 16:36:36 +1200 Subject: [PATCH 05/18] Visible segments --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 43d6dec7..ff4c18ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -170,12 +170,12 @@ script: - cmake .. -DXLNT_CXX_LANG=${CXX_VER} -DSTATIC=$STATIC -DBENCHMARKS=$BENCHMARKS -DSAMPLES=$SAMPLES -DCOVERAGE=$COVERAGE -DCMAKE_BUILD_TYPE=$BUILD_TYPE - cmake --build . -- -j2 - ./tests/xlnt.test - - echo 'travis_fold:start:samples' + - echo "samples" && echo 'travis_fold:start:samples' - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-decrypt; fi - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-img2xlsx "../samples/data/cafe.jpg" "img.xlsx"; fi - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-documentation; fi - echo 'travis_fold:end:samples' - - echo 'travis_fold:start:benchmarks' + - echo "benchmarks" && echo 'travis_fold:start:benchmarks' - if [[ "${BENCHMARKS}" == "ON" ]]; then ./benchmarks/benchmark-styles; fi - if [[ "${BENCHMARKS}" == "ON" ]]; then ./benchmarks/benchmark-writer; fi - echo 'travis_fold:end:benchmarks' From 6a4edfad403c90327fcb14a9f62b625d94948c54 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 28 Jul 2018 16:51:08 +1200 Subject: [PATCH 06/18] Run documentation samples on Appveyor --- .appveyor.yml | 5 +++-- samples/CMakeLists.txt | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a016f954..ade2c3cc 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,6 +18,7 @@ environment: init: [] before_build: + - del /S /F build - mkdir build - cd build - cmake -G "Visual Studio 14 2015" -D CMAKE_GENERATOR_PLATFORM=%platform% -D STATIC=%STATIC% -D SAMPLES=ON -D BENCHMARKS=ON .. @@ -28,5 +29,5 @@ build: verbosity: minimal test_script: - - cd tests\%configuration% - - xlnt.test.exe + - tests\%configuration%\xlnt.test.exe + - samples\%configuration%\sample-documentation.exe diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index f8ad70da..fdeaab4d 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -41,3 +41,4 @@ foreach(SAMPLE_SOURCE IN ITEMS ${SAMPLE_SOURCES}) $) endif() endforeach() +# \ No newline at end of file From e350570ae6e59ffff6319b2944a7d6047808407c Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 28 Jul 2018 18:12:01 +1200 Subject: [PATCH 07/18] Actually build samples + benchmarks on appveyor --- .appveyor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index ade2c3cc..efd72a9d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,17 +14,19 @@ environment: matrix: - STATIC: OFF - STATIC: ON + +matrix: + fast_finish: true init: [] before_build: - - del /S /F build - mkdir build - cd build - cmake -G "Visual Studio 14 2015" -D CMAKE_GENERATOR_PLATFORM=%platform% -D STATIC=%STATIC% -D SAMPLES=ON -D BENCHMARKS=ON .. build: - project: build/tests/xlnt.test.sln + project: build/xlnt_all.sln parallel: true verbosity: minimal From 138c90883b3a7bcb3cb08221030a2882083c2399 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 10:10:36 +1200 Subject: [PATCH 08/18] Modify writer benchmark to make comparisons between column and row usage - Cut time to write a sheet with many rows by not calling highest_row inside a loop over the rows (On^2 -> On) - Observation: more memory is used / cell as the number of rows increases --- benchmarks/writer.cpp | 31 +++++++++---------- source/detail/serialization/xlsx_producer.cpp | 3 +- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/benchmarks/writer.cpp b/benchmarks/writer.cpp index abe94e52..9e59f42e 100644 --- a/benchmarks/writer.cpp +++ b/benchmarks/writer.cpp @@ -39,11 +39,10 @@ void writer(int cols, int rows) for(int index = 0; index < rows; index++) { - if ((index + 1) % (rows / 10) == 0) + if (rows >= 10 && (index + 1) % (rows / 10) == 0) { std::string progress = std::string((index + 1) / (1 + rows / 10), '.'); std::cout << "\r" << progress; - std::cout.flush(); } for (int i = 0; i < cols; i++) @@ -51,8 +50,7 @@ void writer(int cols, int rows) ws.cell(xlnt::cell_reference(i + 1, index + 1)).value(i); } } - - std::cout << std::endl; + std::cout << '\n'; auto filename = "benchmark.xlsx"; wb.save(filename); @@ -63,33 +61,32 @@ void writer(int cols, int rows) // Time from the best of three is taken. void timer(std::function fn, int cols, int rows) { - using xlnt::benchmarks::current_time; - const auto repeat = std::size_t(3); - auto time = std::numeric_limits::max(); - + std::chrono::duration time{}; std::cout << cols << " cols " << rows << " rows" << std::endl; + fn(rows, cols); // 1 cold run for(int i = 0; i < repeat; i++) { - auto start = current_time(); + auto start = std::chrono::high_resolution_clock::now(); fn(cols, rows); - time = std::min(current_time() - start, time); + time += std::chrono::high_resolution_clock::now() - start; } - std::cout << time / 1000.0 << std::endl; + std::cout << time.count() / repeat << " seconds per iteration" << '\n' << '\n'; } } // namespace int main() { - timer(&writer, 100, 100); - timer(&writer, 1000, 100); - timer(&writer, 4000, 100); - timer(&writer, 8192, 100); - timer(&writer, 10, 10000); - timer(&writer, 4000, 1000); + timer(&writer, 160000, 1); + timer(&writer, 6400, 25); + timer(&writer, 1600, 100); + timer(&writer, 400, 400); + timer(&writer, 100, 1600); + timer(&writer, 25, 6400); + timer(&writer, 1, 160000); return 0; } diff --git a/source/detail/serialization/xlsx_producer.cpp b/source/detail/serialization/xlsx_producer.cpp index 212b0d89..94472a2f 100644 --- a/source/detail/serialization/xlsx_producer.cpp +++ b/source/detail/serialization/xlsx_producer.cpp @@ -2180,7 +2180,8 @@ void xlsx_producer::write_worksheet(const relationship &rel) return true; } - for (auto row = ws.lowest_row(); row <= ws.highest_row(); ++row) + auto highest = ws.highest_row(); + for (auto row = ws.lowest_row(); row <= highest; ++row) { if (ws.has_row_properties(row) && ws.row_properties(row).dy_descent.is_set()) { From dd6f33841985303f1f9f9f4f1ef167bde31c1943 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 11:31:38 +1200 Subject: [PATCH 09/18] Remove memory growth based on row count - Nested unordered_map was the cause of a significant memory/cell spike. - added cell_reference hash, lookup by cell_reference to make memory usage proportional to cell count only --- include/xlnt/cell/cell_reference.hpp | 16 ++- .../detail/implementations/worksheet_impl.hpp | 9 +- source/worksheet/worksheet.cpp | 98 +++++++------------ 3 files changed, 54 insertions(+), 69 deletions(-) diff --git a/include/xlnt/cell/cell_reference.hpp b/include/xlnt/cell/cell_reference.hpp index 57fd54bc..fcd32c40 100644 --- a/include/xlnt/cell/cell_reference.hpp +++ b/include/xlnt/cell/cell_reference.hpp @@ -26,7 +26,7 @@ #include #include #include - +#include #include #include @@ -255,3 +255,17 @@ private: }; } // namespace xlnt + +namespace std +{ +template <> +struct hash +{ + size_t operator()(const xlnt::cell_reference &x) const + { + static_assert(std::is_same::value, "this hash function expects both row and column to be 32-bit numbers"); + static_assert(std::is_same::value, "this hash function expects both row and column to be 32-bit numbers"); + return hash{}(x.row() | static_cast(x.column_index()) << 32); + } +}; +} \ No newline at end of file diff --git a/source/detail/implementations/worksheet_impl.hpp b/source/detail/implementations/worksheet_impl.hpp index cb3be2fb..7ae00ae4 100644 --- a/source/detail/implementations/worksheet_impl.hpp +++ b/source/detail/implementations/worksheet_impl.hpp @@ -88,12 +88,9 @@ struct worksheet_impl sheet_properties_ = other.sheet_properties_; print_options_ = other.print_options_; - for (auto &row : cell_map_) + for (auto &cell : cell_map_) { - for (auto &cell : row.second) - { - cell.second.parent_ = this; - } + cell.second.parent_ = this; } } @@ -134,7 +131,7 @@ struct worksheet_impl std::unordered_map column_properties_; std::unordered_map row_properties_; - std::unordered_map> cell_map_; + std::unordered_map cell_map_; optional page_setup_; optional auto_filter_; diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index 5ab28fa1..33386f84 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -196,32 +196,18 @@ const workbook &worksheet::workbook() const void worksheet::garbage_collect() { - auto cell_map_iter = d_->cell_map_.begin(); + auto cell_iter = d_->cell_map_.begin(); - while (cell_map_iter != d_->cell_map_.end()) + while (cell_iter != d_->cell_map_.end()) { - auto cell_iter = cell_map_iter->second.begin(); - - while (cell_iter != cell_map_iter->second.end()) + if (xlnt::cell(&cell_iter->second).garbage_collectible()) { - class cell current_cell(&cell_iter->second); - - if (current_cell.garbage_collectible()) - { - cell_iter = cell_map_iter->second.erase(cell_iter); - continue; - } - - cell_iter++; + cell_iter = d_->cell_map_.erase(cell_iter); } - - if (cell_map_iter->second.empty()) + else { - cell_map_iter = d_->cell_map_.erase(cell_map_iter); - continue; + ++cell_iter; } - - cell_map_iter++; } } @@ -391,25 +377,22 @@ cell_reference worksheet::active_cell() const cell worksheet::cell(const cell_reference &reference) { - auto &row = d_->cell_map_[reference.row()]; - auto match = row.find(reference.column_index()); - - if (match == row.end()) + auto match = d_->cell_map_.find(reference); + if (match == d_->cell_map_.end()) { - match = row.emplace(reference.column_index(), detail::cell_impl()).first; - auto &impl = match->second; - + auto impl = detail::cell_impl(); impl.parent_ = d_; impl.column_ = reference.column_index(); impl.row_ = reference.row(); - } + match = d_->cell_map_.emplace(reference, impl).first; + } return xlnt::cell(&match->second); } const cell worksheet::cell(const cell_reference &reference) const { - return xlnt::cell(&d_->cell_map_.at(reference.row()).at(reference.column_index())); + return xlnt::cell(&d_->cell_map_.at(reference)); } cell worksheet::cell(xlnt::column_t column, row_t row) @@ -424,13 +407,8 @@ const cell worksheet::cell(xlnt::column_t column, row_t row) const bool worksheet::has_cell(const cell_reference &reference) const { - const auto row = d_->cell_map_.find(reference.row()); - if (row == d_->cell_map_.cend()) return false; - - const auto col = row->second.find(reference.column_index()); - if (col == row->second.cend()) return false; - - return true; + const auto cell = d_->cell_map_.find(reference); + return cell != d_->cell_map_.cend(); } bool worksheet::has_row_properties(row_t row) const @@ -477,12 +455,9 @@ column_t worksheet::lowest_column() const auto lowest = constants::max_column(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - for (auto &c : row.second) - { - lowest = std::min(lowest, c.first); - } + lowest = std::min(lowest, cell.first.column()); } return lowest; @@ -514,9 +489,9 @@ row_t worksheet::lowest_row() const auto lowest = constants::max_row(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - lowest = std::min(lowest, row.first); + lowest = std::min(lowest, cell.first.row()); } return lowest; @@ -543,9 +518,9 @@ row_t worksheet::highest_row() const { auto highest = constants::min_row(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - highest = std::max(highest, row.first); + highest = std::max(highest, cell.first.row()); } return highest; @@ -572,12 +547,9 @@ column_t worksheet::highest_column() const { auto highest = constants::min_column(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - for (auto &c : row.second) - { - highest = std::max(highest, c.first); - } + highest = std::max(highest, cell.first.column()); } return highest; @@ -744,13 +716,22 @@ const cell_vector worksheet::cells(bool skip_null) const void worksheet::clear_cell(const cell_reference &ref) { - d_->cell_map_.at(ref.row()).erase(ref.column()); + d_->cell_map_.erase(ref); // TODO: garbage collect newly unreferenced resources such as styles? } void worksheet::clear_row(row_t row) { - d_->cell_map_.erase(row); + for (auto it = d_->cell_map_.begin(); it != d_->cell_map_.end();) + { + if (it->first.row() == row) + { + it = d_->cell_map_.erase(it); + } + else { + ++it; + } + } d_->row_properties_.erase(row); // TODO: garbage collect newly unreferenced resources such as styles? } @@ -769,22 +750,16 @@ bool worksheet::compare(const worksheet &other, bool reference) const if (d_->parent_ != other.d_->parent_) return false; - for (auto &row : d_->cell_map_) - { - if (other.d_->cell_map_.find(row.first) == other.d_->cell_map_.end()) - { - return false; - } - for (auto &cell : row.second) + for (auto &cell : d_->cell_map_) { - if (other.d_->cell_map_[row.first].find(cell.first) == other.d_->cell_map_[row.first].end()) + if (other.d_->cell_map_.find(cell.first) == other.d_->cell_map_.end()) { return false; } xlnt::cell this_cell(&cell.second); - xlnt::cell other_cell(&other.d_->cell_map_[row.first][cell.first]); + xlnt::cell other_cell(&other.d_->cell_map_[cell.first]); if (this_cell.data_type() != other_cell.data_type()) { @@ -797,7 +772,6 @@ bool worksheet::compare(const worksheet &other, bool reference) const return false; } } - } // todo: missing some comparisons From ba54f9eaa39fa1acc454b68d05225a66e3d9f0e0 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 15:05:21 +1200 Subject: [PATCH 10/18] Reduce run-time of benchmark by 16x (160k to 10k cells) --- benchmarks/writer.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/benchmarks/writer.cpp b/benchmarks/writer.cpp index 9e59f42e..acafd27f 100644 --- a/benchmarks/writer.cpp +++ b/benchmarks/writer.cpp @@ -80,13 +80,11 @@ void timer(std::function fn, int cols, int rows) int main() { - timer(&writer, 160000, 1); - timer(&writer, 6400, 25); - timer(&writer, 1600, 100); - timer(&writer, 400, 400); - timer(&writer, 100, 1600); - timer(&writer, 25, 6400); - timer(&writer, 1, 160000); + timer(&writer, 10000, 1); + timer(&writer, 1000, 10); + timer(&writer, 100, 100); + timer(&writer, 10, 1000); + timer(&writer, 1, 10000); return 0; } From 808765ea3938aafd8dee33ca4b557140a8a7cdde Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 15:11:19 +1200 Subject: [PATCH 11/18] format as milliseconds --- benchmarks/writer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/writer.cpp b/benchmarks/writer.cpp index acafd27f..c69f138e 100644 --- a/benchmarks/writer.cpp +++ b/benchmarks/writer.cpp @@ -62,7 +62,7 @@ void writer(int cols, int rows) void timer(std::function fn, int cols, int rows) { const auto repeat = std::size_t(3); - std::chrono::duration time{}; + std::chrono::duration time{}; std::cout << cols << " cols " << rows << " rows" << std::endl; fn(rows, cols); // 1 cold run @@ -73,7 +73,7 @@ void timer(std::function fn, int cols, int rows) time += std::chrono::high_resolution_clock::now() - start; } - std::cout << time.count() / repeat << " seconds per iteration" << '\n' << '\n'; + std::cout << time.count() / repeat << " ms per iteration" << '\n' << '\n'; } } // namespace From f64dbc00c4881b51b14c3f32d41fbe3081876870 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 16:24:09 +1200 Subject: [PATCH 12/18] string compare is slow, use rgba member instead --- source/styles/color.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/styles/color.cpp b/source/styles/color.cpp index 141616fc..6799ac27 100644 --- a/source/styles/color.cpp +++ b/source/styles/color.cpp @@ -301,7 +301,7 @@ bool color::operator==(const xlnt::color &other) const case color_type::theme: return theme_.index() == other.theme_.index(); case color_type::rgb: - return rgb_.hex_string() == other.rgb_.hex_string(); + return rgb_.rgba() == rgb_.rgba(); } return false; From 5a5db1a50b2972fa50fb6268165610a8142be8a3 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 16:48:18 +1200 Subject: [PATCH 13/18] use std::find and don't double iterate the list - ~5% perf improvement in img2xlsx example --- source/detail/implementations/stylesheet.hpp | 44 +++++++------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/source/detail/implementations/stylesheet.hpp b/source/detail/implementations/stylesheet.hpp index 8ccc5976..0dd091fb 100644 --- a/source/detail/implementations/stylesheet.hpp +++ b/source/detail/implementations/stylesheet.hpp @@ -178,32 +178,15 @@ struct stylesheet } template - std::size_t find_or_add(C &container, const T &item, bool *added = nullptr) + std::size_t find_or_add(C &container, const T &item) { - if (added != nullptr) - { - *added = false; - } - std::size_t i = 0; - - for (auto iter = container.begin(); iter != container.end(); ++iter) + auto iter = std::find(container.begin(), container.end(), item); + if (iter != container.end()) { - if (*iter == item) - { - return i; - } - - ++i; + return iter - container.begin(); } - - if (added != nullptr) - { - *added = true; - } - - container.emplace(container.end(), item); - + container.emplace_back(item); return container.size() - 1; } @@ -235,7 +218,6 @@ struct stylesheet if (!garbage_collection_enabled) return; auto format_iter = format_impls.begin(); - while (format_iter != format_impls.end()) { auto &impl = *format_iter; @@ -399,12 +381,18 @@ struct stylesheet format_impl *find_or_create(format_impl &pattern) { - auto iter = format_impls.begin(); - bool added = false; pattern.references = 0; - auto id = find_or_add(format_impls, pattern, &added); - std::advance(iter, static_cast::difference_type>(id)); - + auto id = 0; + auto iter = format_impls.begin(); + while (iter != format_impls.end() && !(*iter == pattern)) + { + ++id; + ++iter; + } + if (iter == format_impls.end()) + { + iter = format_impls.emplace(format_impls.end(), pattern); + } auto &result = *iter; result.parent = this; From b4f695b3eaf015c649076efb7b29f350c66fe3d3 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 17:07:21 +1200 Subject: [PATCH 14/18] fixing warnings --- source/detail/implementations/stylesheet.hpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/detail/implementations/stylesheet.hpp b/source/detail/implementations/stylesheet.hpp index 0dd091fb..5b14b8eb 100644 --- a/source/detail/implementations/stylesheet.hpp +++ b/source/detail/implementations/stylesheet.hpp @@ -180,14 +180,16 @@ struct stylesheet template std::size_t find_or_add(C &container, const T &item) { - std::size_t i = 0; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" auto iter = std::find(container.begin(), container.end(), item); if (iter != container.end()) { - return iter - container.begin(); + return std::size_t(iter - container.begin()); } - container.emplace_back(item); - return container.size() - 1; + iter = container.emplace(container.end(), item); + return std::size_t(iter - container.begin()); +#pragma GCC diagnostic pop } template @@ -382,7 +384,7 @@ struct stylesheet format_impl *find_or_create(format_impl &pattern) { pattern.references = 0; - auto id = 0; + std::size_t id = 0; auto iter = format_impls.begin(); while (iter != format_impls.end() && !(*iter == pattern)) { From e6a84c0cf0d09cba10f37270836531a91e8dbb10 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 22:10:56 +1200 Subject: [PATCH 15/18] Optimise modifying a new format to reduce garbage collection runs --- source/detail/implementations/stylesheet.hpp | 35 ++++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/source/detail/implementations/stylesheet.hpp b/source/detail/implementations/stylesheet.hpp index 5b14b8eb..0ec45161 100644 --- a/source/detail/implementations/stylesheet.hpp +++ b/source/detail/implementations/stylesheet.hpp @@ -416,7 +416,10 @@ struct stylesheet { format_impl new_format = *pattern; new_format.style = style_name; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -425,7 +428,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.alignment_id = find_or_add(alignments, new_alignment); new_format.alignment_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -434,7 +440,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.border_id = find_or_add(borders, new_border); new_format.border_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -443,7 +452,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.fill_id = find_or_add(fills, new_fill); new_format.fill_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -452,7 +464,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.font_id = find_or_add(fonts, new_font); new_format.font_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -465,7 +480,10 @@ struct stylesheet } new_format.number_format_id = new_number_format.id(); new_format.number_format_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -474,7 +492,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.protection_id = find_or_add(protections, new_protection); new_format.protection_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } From 3ae31fadc31558cb855f1181823f5c6fef0420fa Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sun, 29 Jul 2018 23:28:02 +1200 Subject: [PATCH 16/18] Another easy 15-20% by not searching with no references --- source/cell/cell.cpp | 3 +-- source/detail/implementations/stylesheet.hpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp index 667212d8..98dc1739 100644 --- a/source/cell/cell.cpp +++ b/source/cell/cell.cpp @@ -750,8 +750,7 @@ void cell::format(const class format new_format) format().d_->references -= format().d_->references > 0 ? 1 : 0; } - ++new_format.d_->references; - d_->format_ = new_format.d_; + d_->format_ = detail::reference(new_format.d_); } calendar cell::base_date() const diff --git a/source/detail/implementations/stylesheet.hpp b/source/detail/implementations/stylesheet.hpp index 0ec45161..9863909d 100644 --- a/source/detail/implementations/stylesheet.hpp +++ b/source/detail/implementations/stylesheet.hpp @@ -419,6 +419,7 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; + return pattern; } return find_or_create(new_format); } @@ -431,6 +432,7 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; + return pattern; } return find_or_create(new_format); } @@ -443,6 +445,7 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; + return pattern; } return find_or_create(new_format); } @@ -455,6 +458,7 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; + return pattern; } return find_or_create(new_format); } @@ -467,6 +471,7 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; + return pattern; } return find_or_create(new_format); } @@ -483,6 +488,7 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; + return pattern; } return find_or_create(new_format); } @@ -495,6 +501,7 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; + return pattern; } return find_or_create(new_format); } @@ -577,5 +584,15 @@ struct stylesheet std::vector colors; }; +inline format_impl* reference(format_impl *impl) +{ + ++impl->references; + if (impl->references == 1) + { + impl = impl->parent->find_or_create(*impl); + } + return impl; +} + } // namespace detail } // namespace xlnt From 544e90c975d178762bf36815181de1667ee854ab Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Mon, 30 Jul 2018 07:07:32 +1200 Subject: [PATCH 17/18] fix spectacular typo - Using rgba for comparison is still faster, but it does help correctness when you compare two different instances --- source/styles/color.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/styles/color.cpp b/source/styles/color.cpp index 6799ac27..5b6ae876 100644 --- a/source/styles/color.cpp +++ b/source/styles/color.cpp @@ -301,7 +301,7 @@ bool color::operator==(const xlnt::color &other) const case color_type::theme: return theme_.index() == other.theme_.index(); case color_type::rgb: - return rgb_.rgba() == rgb_.rgba(); + return rgb_.rgba() == other.rgb_.rgba(); } return false; From c3296db932a9ef5bfa49b30ca995cf0bc5f466f9 Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Mon, 30 Jul 2018 07:22:45 +1200 Subject: [PATCH 18/18] Revert "Another easy 15-20% by not searching with no references" This reverts commit 3ae31fadc31558cb855f1181823f5c6fef0420fa. --- source/cell/cell.cpp | 3 ++- source/detail/implementations/stylesheet.hpp | 17 ----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp index 98dc1739..667212d8 100644 --- a/source/cell/cell.cpp +++ b/source/cell/cell.cpp @@ -750,7 +750,8 @@ void cell::format(const class format new_format) format().d_->references -= format().d_->references > 0 ? 1 : 0; } - d_->format_ = detail::reference(new_format.d_); + ++new_format.d_->references; + d_->format_ = new_format.d_; } calendar cell::base_date() const diff --git a/source/detail/implementations/stylesheet.hpp b/source/detail/implementations/stylesheet.hpp index 9863909d..0ec45161 100644 --- a/source/detail/implementations/stylesheet.hpp +++ b/source/detail/implementations/stylesheet.hpp @@ -419,7 +419,6 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; - return pattern; } return find_or_create(new_format); } @@ -432,7 +431,6 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; - return pattern; } return find_or_create(new_format); } @@ -445,7 +443,6 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; - return pattern; } return find_or_create(new_format); } @@ -458,7 +455,6 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; - return pattern; } return find_or_create(new_format); } @@ -471,7 +467,6 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; - return pattern; } return find_or_create(new_format); } @@ -488,7 +483,6 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; - return pattern; } return find_or_create(new_format); } @@ -501,7 +495,6 @@ struct stylesheet if (pattern->references == 0) { *pattern = new_format; - return pattern; } return find_or_create(new_format); } @@ -584,15 +577,5 @@ struct stylesheet std::vector colors; }; -inline format_impl* reference(format_impl *impl) -{ - ++impl->references; - if (impl->references == 1) - { - impl = impl->parent->find_or_create(*impl); - } - return impl; -} - } // namespace detail } // namespace xlnt