From 9462a66c1b94192ba36f20d02e0d4ec18df7b7ec Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 15:12:02 +1000 Subject: [PATCH 01/12] test(stlsoft): benchmark platformstl::file_lines Add a standalone comparative performance test for platformstl::file_lines_a and std::ifstream plus std::getline; follow existing STLSoft warm-up, stopwatch, anchoring, and tabular output conventions; --- test/performance/CMakeLists.txt | 1 + .../CMakeLists.txt | 2 + .../main.cpp | 237 ++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 test/performance/test.performance.platformstl.file_lines/CMakeLists.txt create mode 100644 test/performance/test.performance.platformstl.file_lines/main.cpp diff --git a/test/performance/CMakeLists.txt b/test/performance/CMakeLists.txt index 1e025c0e..e107cf58 100644 --- a/test/performance/CMakeLists.txt +++ b/test/performance/CMakeLists.txt @@ -1,6 +1,7 @@ if(X_CMAKE_CXX_FULLSTANDARD GREATER_EQUAL 2011) + add_subdirectory(test.performance.platformstl.file_lines) add_subdirectory(test.performance.platformstl.stopwatch) endif() diff --git a/test/performance/test.performance.platformstl.file_lines/CMakeLists.txt b/test/performance/test.performance.platformstl.file_lines/CMakeLists.txt new file mode 100644 index 00000000..d76ef1eb --- /dev/null +++ b/test/performance/test.performance.platformstl.file_lines/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_example_program(test.performance.platformstl.file_lines main.cpp) diff --git a/test/performance/test.performance.platformstl.file_lines/main.cpp b/test/performance/test.performance.platformstl.file_lines/main.cpp new file mode 100644 index 00000000..c66fa607 --- /dev/null +++ b/test/performance/test.performance.platformstl.file_lines/main.cpp @@ -0,0 +1,237 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test.performance.platformstl.file_lines/main.cpp + * + * Purpose: Comparative perf-test for platformstl::file_lines and + * std::ifstream + std::getline. + * + * Created: 23rd September 2026 + * Updated: 23rd September 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * types + */ + +typedef stlsoft::std_chrono_hrc_stopwatch stopwatch_t; +typedef stopwatch_t::interval_type interval_t; + +typedef std::pair< + interval_t +, std::size_t +> result_t; + + +/* ///////////////////////////////////////////////////////////////////////// + * constants + */ + +namespace { + +char const TEST_FILE_NAME[] = "test.performance.platformstl.file_lines.txt"; + +const std::size_t NUM_ITERATIONS = 1000; +const std::size_t NUM_WARMUPS = 2; + +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * helpers + */ + +std::string +make_line( + std::size_t length +) +{ + std::string line(length, 'x'); + + for (std::size_t i = 0; length != i; ++i) + { + line[i] = static_cast('a' + (i % 26)); + } + + return line; +} + +bool +write_test_file( + std::size_t num_lines +, std::size_t line_length +) +{ + std::ofstream stm(TEST_FILE_NAME, std::ios::binary | std::ios::trunc); + + if (!stm) + { + return false; + } + + std::string const line = make_line(line_length); + + for (std::size_t i = 0; num_lines != i; ++i) + { + stm << line << '\n'; + } + + return static_cast(stm); +} + +template +result_t +time_( + std::size_t num_iterations +, F fn +) +{ + interval_t interval = 0; + std::size_t anchor = 0; + + for (std::size_t w = NUM_WARMUPS; 0 != w; --w) + { + stopwatch_t sw; + + anchor = 0; + sw.start(); + + for (std::size_t i = 0; num_iterations != i; ++i) + { + anchor += fn(); + } + + sw.stop(); + + if (1 == w) + { + interval = sw.get_nanoseconds(); + } + } + + return std::make_pair(interval, anchor); +} + +void +display_result( + char const* scenario +, char const* implementation +, std::size_t num_iterations +, result_t const& result +) +{ + std::cout + << '\t' + << scenario + << '\t' + << implementation + << '\t' + << num_iterations + << '\t' + << std::setw(16) << std::right << result.first + << '\t' + << std::setw(12) << std::right << std::fixed << std::setprecision(3) + << (static_cast(result.first) / num_iterations) + << '\t' + << result.second + << std::endl; +} + +void +run_scenario( + std::size_t num_lines +, std::size_t line_length +) +{ + char scenario[64]; + + std::snprintf( + scenario + , sizeof(scenario) + , "%lu lines x %lu chars" + , static_cast(num_lines) + , static_cast(line_length) + ); + + if (!write_test_file(num_lines, line_length)) + { + std::cerr + << "failed to write " + << TEST_FILE_NAME + << std::endl + ; + + return; + } + + result_t const file_lines = time_(NUM_ITERATIONS, []() -> std::size_t { + platformstl::file_lines_a lines(TEST_FILE_NAME); + std::size_t anchor = lines.size(); + + for (platformstl::file_lines_a::const_iterator i = lines.begin(); lines.end() != i; ++i) + { + anchor += (*i).size(); + } + + return anchor; + }); + + result_t const getline = time_(NUM_ITERATIONS, []() -> std::size_t { + std::ifstream stm(TEST_FILE_NAME); + std::vector lines; + std::string line; + std::size_t anchor = 0; + + while (std::getline(stm, line)) + { + anchor += line.size(); + lines.push_back(line); + } + + return lines.size() + anchor; + }); + + display_result(scenario, "platformstl::file_lines", NUM_ITERATIONS, file_lines); + display_result(scenario, "std::ifstream+getline", NUM_ITERATIONS, getline); +} + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int /*argc*/, char* /*argv*/[]) +{ + std::cout + << "scenario\timplementation\titerations\ttotal-ns\tns/op\tanchor" + << std::endl + ; + + run_scenario(1000, 64); + run_scenario(5000, 80); + + std::remove(TEST_FILE_NAME); + + return EXIT_SUCCESS; +} + + +/* ///////////////////////////// end of file //////////////////////////// */ + From 470f6fef7e1b2a30fbe3d149c61731d022339643 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 16:29:20 +1000 Subject: [PATCH 02/12] test(platformstl): improved performance test program for `platformstl::basic_file_lines<>` --- .../main.cpp | 116 +++++++++++++----- 1 file changed, 88 insertions(+), 28 deletions(-) diff --git a/test/performance/test.performance.platformstl.file_lines/main.cpp b/test/performance/test.performance.platformstl.file_lines/main.cpp index c66fa607..7a2e752a 100644 --- a/test/performance/test.performance.platformstl.file_lines/main.cpp +++ b/test/performance/test.performance.platformstl.file_lines/main.cpp @@ -16,13 +16,19 @@ #include #include +#include +#include +#include #include #include #include #include #include #include +#if __cplusplus >= 201703L +# include +#endif /* C++17+ */ #include #include @@ -39,6 +45,26 @@ typedef std::pair< , std::size_t > result_t; +typedef platformstl::basic_file_lines< + char +, std::string +> file_lines_std_string_t; +typedef platformstl::basic_file_lines< + char +, stlsoft::simple_string +> file_lines_stlsoft_simple_string_t; +typedef platformstl::basic_file_lines< + char +, stlsoft::string_view +> file_lines_stlsoft_string_view_t; +#if __cplusplus >= 201703L + +typedef platformstl::basic_file_lines< + char +, std::string_view +> file_lines_std_string_view_t; +#endif /* C++17+ */ + /* ///////////////////////////////////////////////////////////////////////// * constants @@ -48,6 +74,12 @@ namespace { char const TEST_FILE_NAME[] = "test.performance.platformstl.file_lines.txt"; +std::size_t const COLUMN_WIDTH_ANCHOR = 12; +std::size_t const COLUMN_WIDTH_IMPLEMENTATION = 48; +std::size_t const COLUMN_WIDTH_ITERATIONS = 12; +std::size_t const COLUMN_WIDTH_NS_PER_OPERATION = 12; +std::size_t const COLUMN_WIDTH_SCENARIO = 24; +std::size_t const COLUMN_WIDTH_TOTAL_TIME = 16; const std::size_t NUM_ITERATIONS = 1000; const std::size_t NUM_WARMUPS = 2; @@ -96,6 +128,21 @@ write_test_file( return static_cast(stm); } +template +std::size_t +read_file_lines_() +{ + T_file_lines lines(TEST_FILE_NAME); + std::size_t anchor = lines.size(); + + for (typename T_file_lines::const_iterator i = lines.begin(); lines.end() != i; ++i) + { + anchor += (*i).size(); + } + + return anchor; +} + template result_t time_( @@ -138,19 +185,15 @@ display_result( ) { std::cout - << '\t' - << scenario - << '\t' - << implementation - << '\t' - << num_iterations - << '\t' - << std::setw(16) << std::right << result.first - << '\t' - << std::setw(12) << std::right << std::fixed << std::setprecision(3) + << std::left + << std::setw(COLUMN_WIDTH_SCENARIO) << scenario + << std::setw(COLUMN_WIDTH_IMPLEMENTATION) << implementation + << std::right + << std::setw(COLUMN_WIDTH_ITERATIONS) << num_iterations + << std::setw(COLUMN_WIDTH_TOTAL_TIME) << result.first + << std::setw(COLUMN_WIDTH_NS_PER_OPERATION) << std::fixed << std::setprecision(3) << (static_cast(result.first) / num_iterations) - << '\t' - << result.second + << std::setw(COLUMN_WIDTH_ANCHOR) << result.second << std::endl; } @@ -162,7 +205,7 @@ run_scenario( { char scenario[64]; - std::snprintf( + stlsoft::snprintf( scenario , sizeof(scenario) , "%lu lines x %lu chars" @@ -181,18 +224,6 @@ run_scenario( return; } - result_t const file_lines = time_(NUM_ITERATIONS, []() -> std::size_t { - platformstl::file_lines_a lines(TEST_FILE_NAME); - std::size_t anchor = lines.size(); - - for (platformstl::file_lines_a::const_iterator i = lines.begin(); lines.end() != i; ++i) - { - anchor += (*i).size(); - } - - return anchor; - }); - result_t const getline = time_(NUM_ITERATIONS, []() -> std::size_t { std::ifstream stm(TEST_FILE_NAME); std::vector lines; @@ -208,8 +239,30 @@ run_scenario( return lines.size() + anchor; }); - display_result(scenario, "platformstl::file_lines", NUM_ITERATIONS, file_lines); - display_result(scenario, "std::ifstream+getline", NUM_ITERATIONS, getline); + result_t const file_lines_std_string = time_(NUM_ITERATIONS, []() -> std::size_t { + return read_file_lines_(); + }); + + result_t const file_lines_stlsoft_simple_string = time_(NUM_ITERATIONS, []() -> std::size_t { + return read_file_lines_(); + }); + + result_t const file_lines_stlsoft_string_view = time_(NUM_ITERATIONS, []() -> std::size_t { + return read_file_lines_(); + }); + + display_result(scenario, "vector+getline", NUM_ITERATIONS, getline); + display_result(scenario, "basic_file_lines", NUM_ITERATIONS, file_lines_std_string); + display_result(scenario, "basic_file_lines", NUM_ITERATIONS, file_lines_stlsoft_simple_string); + display_result(scenario, "basic_file_lines", NUM_ITERATIONS, file_lines_stlsoft_string_view); +#if __cplusplus >= 201703L + + result_t const file_lines_std_string_view = time_(NUM_ITERATIONS, []() -> std::size_t { + return read_file_lines_(); + }); + + display_result(scenario, "basic_file_lines", NUM_ITERATIONS, file_lines_std_string_view); +#endif /* C++17+ */ } @@ -220,7 +273,14 @@ run_scenario( int main(int /*argc*/, char* /*argv*/[]) { std::cout - << "scenario\timplementation\titerations\ttotal-ns\tns/op\tanchor" + << std::left + << std::setw(COLUMN_WIDTH_SCENARIO) << "scenario" + << std::setw(COLUMN_WIDTH_IMPLEMENTATION) << "implementation" + << std::right + << std::setw(COLUMN_WIDTH_ITERATIONS) << "iterations" + << std::setw(COLUMN_WIDTH_TOTAL_TIME) << "total-ns" + << std::setw(COLUMN_WIDTH_NS_PER_OPERATION) << "ns/op" + << std::setw(COLUMN_WIDTH_ANCHOR) << "anchor" << std::endl ; From 989aba2cab6f982c0e504642cdcb80d3658e4bd1 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 16:32:50 +1000 Subject: [PATCH 03/12] fix(platformstl::file_lines): compatible with `std::basic_string_view` --- include/platformstl/filesystem/file_lines.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/platformstl/filesystem/file_lines.hpp b/include/platformstl/filesystem/file_lines.hpp index b8574eab..527ea724 100644 --- a/include/platformstl/filesystem/file_lines.hpp +++ b/include/platformstl/filesystem/file_lines.hpp @@ -4,7 +4,7 @@ * Purpose: Platform header for the file_lines components. * * Created: 25th October 2007 - * Updated: 20th March 2025 + * Updated: 23rd September 2026 * * Home: http://stlsoft.org/ * @@ -48,8 +48,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_MAJOR 2 # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_MINOR 1 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_REVISION 1 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_EDIT 52 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_REVISION 2 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_EDIT 53 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /** \file platformstl/filesystem/file_lines.hpp @@ -432,7 +432,7 @@ class basic_file_lines { --eol; - m_strings.push_back(value_string_type_(s0, eol)); + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); s0 = begin; } @@ -443,7 +443,7 @@ class basic_file_lines --eol; } - m_strings.push_back(value_string_type_(s0, eol)); + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); s0 = begin + 1; break; @@ -452,7 +452,7 @@ class basic_file_lines { --eol; - m_strings.push_back(value_string_type_(s0, eol)); + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); s0 = begin; } @@ -470,7 +470,7 @@ class basic_file_lines --eol; } - m_strings.push_back(value_string_type_(s0, eol)); + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); } #endif From 4ec93f32811610f89338e5e389696644cd2741ba Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 16:34:13 +1000 Subject: [PATCH 04/12] perf(platformstl::file_lines): adjusted preallocation of number of reserved lines --- include/platformstl/filesystem/file_lines.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/platformstl/filesystem/file_lines.hpp b/include/platformstl/filesystem/file_lines.hpp index 527ea724..ea1b4493 100644 --- a/include/platformstl/filesystem/file_lines.hpp +++ b/include/platformstl/filesystem/file_lines.hpp @@ -106,6 +106,9 @@ #ifndef STLSOFT_INCL_STLSOFT_STRING_HPP_SIMPLE_STRING # include #endif /* !STLSOFT_INCL_STLSOFT_STRING_HPP_SIMPLE_STRING */ +#ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_MINMAX +# include +#endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_MINMAX */ #ifndef STLSOFT_INCL_ALGORITHM # define STLSOFT_INCL_ALGORITHM @@ -407,7 +410,9 @@ class basic_file_lines // 3. Parse the file, and populate the strings collection - m_strings.reserve(1u + (cch / 10u)); + size_t const numReserved = maximum(static_cast(128), 1u + (cch / 10u)); + + m_strings.reserve(numReserved); // This can work with EOL of CRLF or of LF, or a combination of the // two. From af467f77738e5a5c8aababaed253a23eb496f4a3 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 16:37:20 +1000 Subject: [PATCH 05/12] perf(platformstl::file_lines): moved file-contents check into in-loop processing, so only pass once --- include/platformstl/filesystem/file_lines.hpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/platformstl/filesystem/file_lines.hpp b/include/platformstl/filesystem/file_lines.hpp index ea1b4493..90b9d832 100644 --- a/include/platformstl/filesystem/file_lines.hpp +++ b/include/platformstl/filesystem/file_lines.hpp @@ -392,16 +392,6 @@ class basic_file_lines size_t cch = static_cast(cb / sizeof(char_type)); #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */ - // check if it looks like a binary file - if (base + cch != std::find(base, base + cch, '\0')) - { -#ifdef STLSOFT_CF_EXCEPTION_SUPPORT - STLSOFT_THROW_X(invalid_file_type_exception("file is binary (or unsupported text encoding)", 0, path)); -#else /* STLSOFT_CF_EXCEPTION_SUPPORT */ - return; -#endif /* STLSOFT_CF_EXCEPTION_SUPPORT */ - } - // 2. Create the contents string m_contents = base_string_type_(base, cch); @@ -422,7 +412,6 @@ class basic_file_lines char_type const* const end = begin + cch; char_type const* s0 = begin; char_type prev = '\0'; -// bool const hasLF = (base + cch != std::find(base, base + cch, '\n')); #if 1 { for (; begin != end; ++begin) @@ -432,6 +421,17 @@ class basic_file_lines switch (c) { + case '\0': + + // reject if it looks like a binary file + +#ifdef STLSOFT_CF_EXCEPTION_SUPPORT + + STLSOFT_THROW_X(invalid_file_type_exception("file is binary (or unsupported text encoding)", 0, path)); +#else /* STLSOFT_CF_EXCEPTION_SUPPORT */ + + return; +#endif /* STLSOFT_CF_EXCEPTION_SUPPORT */ case '\r': if ('\r' == prev) { From 8d3787f5f3c47768b66962dfe8d724b49fb76414 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 17:10:13 +1000 Subject: [PATCH 06/12] perf(platformstl::file_lines): use of `#emplace_back()` (C++ 11+) --- include/platformstl/filesystem/file_lines.hpp | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/include/platformstl/filesystem/file_lines.hpp b/include/platformstl/filesystem/file_lines.hpp index 90b9d832..079622f7 100644 --- a/include/platformstl/filesystem/file_lines.hpp +++ b/include/platformstl/filesystem/file_lines.hpp @@ -48,8 +48,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_MAJOR 2 # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_MINOR 1 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_REVISION 2 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_EDIT 53 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_REVISION 3 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_EDIT 54 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /** \file platformstl/filesystem/file_lines.hpp @@ -413,7 +413,6 @@ class basic_file_lines char_type const* s0 = begin; char_type prev = '\0'; -#if 1 { for (; begin != end; ++begin) { char_type const c = *begin; @@ -437,7 +436,13 @@ class basic_file_lines { --eol; +#if __cplusplus >= 201103L + + m_strings.emplace_back(value_string_type_(s0, static_cast(eol - s0))); +#else /* ? C++ 11+ */ + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); +#endif /* C++ 11+ */ s0 = begin; } @@ -448,7 +453,13 @@ class basic_file_lines --eol; } +#if __cplusplus >= 201103L + + m_strings.emplace_back(value_string_type_(s0, static_cast(eol - s0))); +#else /* ? C++ 11+ */ + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); +#endif /* C++ 11+ */ s0 = begin + 1; break; @@ -457,7 +468,13 @@ class basic_file_lines { --eol; +#if __cplusplus >= 201103L + + m_strings.emplace_back(value_string_type_(s0, static_cast(eol - s0))); +#else /* ? C++ 11+ */ + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); +#endif /* C++ 11+ */ s0 = begin; } @@ -475,9 +492,14 @@ class basic_file_lines --eol; } +#if __cplusplus >= 201103L + + m_strings.emplace_back(value_string_type_(s0, static_cast(eol - s0))); +#else /* ? C++ 11+ */ + m_strings.push_back(value_string_type_(s0, static_cast(eol - s0))); +#endif /* C++ 11+ */ } -#endif // Now determine whether we require the ongoing presence of the // underlying mapping. We can discard it if: From 90b2e1e62786d57bc40d8b64e67c44392a84086d Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 17:30:30 +1000 Subject: [PATCH 07/12] feat(platformstl): add FILE_stream::write_binary() Add a byte-oriented write that preserves embedded NULs, and deprecate write(void const*, size_t) in its favour. Cover CR and CRLF files in the file_lines component tests, and write the NUL fixture through write_binary(). --- .../platformstl/filesystem/FILE_stream.hpp | 29 +++- .../entry.cpp | 126 +++++++++++++++++- 2 files changed, 143 insertions(+), 12 deletions(-) diff --git a/include/platformstl/filesystem/FILE_stream.hpp b/include/platformstl/filesystem/FILE_stream.hpp index d30fd1b2..5710ce5d 100644 --- a/include/platformstl/filesystem/FILE_stream.hpp +++ b/include/platformstl/filesystem/FILE_stream.hpp @@ -4,11 +4,11 @@ * Purpose: Facade for the standard C Streams API. * * Created: 31st May 2009 - * Updated: 20th March 2025 + * Updated: 23rd September 2026 * * Home: http://stlsoft.org/ * - * Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems + * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems * Copyright (c) 2009-2019, Matthew Wilson and Synesis Software * All rights reserved. * @@ -53,8 +53,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_MAJOR 2 # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_MINOR 1 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_REVISION 5 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_EDIT 28 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_REVISION 6 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_EDIT 29 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -270,12 +270,31 @@ class FILE_stream_base return write_text_(ps, cch); } + /// [DEPRECATED] Writes \c cb bytes from the memory block pointed to by + /// \c pv to the underlying file stream. + /// + /// \deprecated Use write_binary() instead + /// + /// \exception X Thrown in \c cb bytes cannot be written to the + /// underlying stream + STLSOFT_DEPRECATED_("write(void const*, size_t) is deprecated and will be removed from a future version of STLSoft; use write_binary() instead") + class_type& write(void const* pv, size_type cb) + { + return write_binary(pv, cb); + } + /// Writes \c cb bytes from the memory block pointed to by \c pv to the /// underlying file stream. /// + /// \note Every byte is written, including embedded NULs. Character + /// overloads of write() stop at the first NUL. Newline + /// translation follows the stream's open mode, so open with + /// "wb" when the stored bytes must match the memory + /// block. + /// /// \exception X Thrown in \c cb bytes cannot be written to the /// underlying stream - class_type& write(void const* pv, size_type cb) + class_type& write_binary(void const* pv, size_type cb) { return write_bytes_(pv, cb); } diff --git a/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/entry.cpp b/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/entry.cpp index 8ec163cb..9628b986 100644 --- a/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/entry.cpp +++ b/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/entry.cpp @@ -32,10 +32,6 @@ #include #include -/* Standard C++ header files */ -#include -#include - /* Standard C header files */ #include @@ -50,6 +46,12 @@ namespace { void TEST_FILE_WITH_ONE_LINE_UNTERMINATED(); void TEST_FILE_WITH_ONE_LINE_TERMINATED(); void TEST_FILE_WITH_THREE_LINES(); + void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CRLF(); + void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CRLF(); + void TEST_FILE_WITH_CONSECUTIVE_CRLF_GIVES_EMPTY_LINE(); + void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CR(); + void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CR(); + void TEST_FILE_WITH_CONSECUTIVE_CR_GIVES_EMPTY_LINE(); void TEST_FILE_CONTAINING_NUL_CHARACTER_THROWS(); } // anonymous namespace @@ -71,6 +73,12 @@ int main(int argc, char* argv[]) XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_UNTERMINATED); XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_TERMINATED); XTESTS_RUN_CASE(TEST_FILE_WITH_THREE_LINES); + XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CRLF); + XTESTS_RUN_CASE(TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CRLF); + XTESTS_RUN_CASE(TEST_FILE_WITH_CONSECUTIVE_CRLF_GIVES_EMPTY_LINE); + XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CR); + XTESTS_RUN_CASE(TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CR); + XTESTS_RUN_CASE(TEST_FILE_WITH_CONSECUTIVE_CR_GIVES_EMPTY_LINE); XTESTS_RUN_CASE_THAT_THROWS(TEST_FILE_CONTAINING_NUL_CHARACTER_THROWS, platformstl::invalid_file_type_exception); XTESTS_PRINT_RESULTS(); @@ -151,15 +159,119 @@ void TEST_FILE_WITH_THREE_LINES() TEST_MS_EQ("ghi", lines[2]); } +void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CRLF() +{ + temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen); + + { + platformstl::FILE_stream stm(f.c_str(), "wb"); + + stm.write("abc\r\n"); + } + + platformstl::file_lines lines(f.c_str()); + + REQUIRE(TEST_INT_EQ(1u, lines.size())); + TEST_MS_EQ("abc", lines[0]); +} + +void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CRLF() +{ + temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen); + + { + platformstl::FILE_stream stm(f.c_str(), "wb"); + + stm.write("abc\r\ndef\r\nghi\r\n"); + } + + platformstl::file_lines lines(f.c_str()); + + REQUIRE(TEST_INT_EQ(3u, lines.size())); + TEST_MS_EQ("abc", lines[0]); + TEST_MS_EQ("def", lines[1]); + TEST_MS_EQ("ghi", lines[2]); +} + +void TEST_FILE_WITH_CONSECUTIVE_CRLF_GIVES_EMPTY_LINE() +{ + temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen); + + { + platformstl::FILE_stream stm(f.c_str(), "wb"); + + stm.write("abc\r\n\r\nghi"); + } + + platformstl::file_lines lines(f.c_str()); + + REQUIRE(TEST_INT_EQ(3u, lines.size())); + TEST_MS_EQ("abc", lines[0]); + TEST_MS_EQ("", lines[1]); + TEST_MS_EQ("ghi", lines[2]); +} + +void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CR() +{ + temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen); + + { + platformstl::FILE_stream stm(f.c_str(), "wb"); + + stm.write("abc\r"); + } + + platformstl::file_lines lines(f.c_str()); + + REQUIRE(TEST_INT_EQ(1u, lines.size())); + TEST_MS_EQ("abc", lines[0]); +} + +void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CR() +{ + temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen); + + { + platformstl::FILE_stream stm(f.c_str(), "wb"); + + stm.write("abc\rdef\rghi\r"); + } + + platformstl::file_lines lines(f.c_str()); + + REQUIRE(TEST_INT_EQ(3u, lines.size())); + TEST_MS_EQ("abc", lines[0]); + TEST_MS_EQ("def", lines[1]); + TEST_MS_EQ("ghi", lines[2]); +} + +void TEST_FILE_WITH_CONSECUTIVE_CR_GIVES_EMPTY_LINE() +{ + temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen); + + { + platformstl::FILE_stream stm(f.c_str(), "wb"); + + stm.write("abc\r\rghi"); + } + + platformstl::file_lines lines(f.c_str()); + + REQUIRE(TEST_INT_EQ(3u, lines.size())); + TEST_MS_EQ("abc", lines[0]); + TEST_MS_EQ("", lines[1]); + TEST_MS_EQ("ghi", lines[2]); +} + void TEST_FILE_CONTAINING_NUL_CHARACTER_THROWS() { temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen); { - std::ofstream stm(f.c_str(), std::ios::binary | std::ios::trunc); - std::string const contents("abc\0def", 7); + char const contents[] = { 'a', 'b', 'c', '\0', 'd', 'e', 'f' }; + platformstl::FILE_stream stm(f.c_str(), "wb"); - stm.write(contents.data(), static_cast(contents.size())); + stm.write_binary(contents, sizeof(contents)); } platformstl::file_lines lines(f.c_str()); From 015a99670743315561d177bcfdae255513ae2e93 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 17:42:40 +1000 Subject: [PATCH 08/12] test(platformstl): cover CR, CRLF, and small files in file_lines perf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Time LF, CRLF, and CR at the existing 1000×64 and 5000×80 shapes, and at 1×16, 8×32, and 32×64, so a later small-file path has a fixed-cost region to beat. Read the getline baseline in binary mode so the CR bytes are the same on every platform. --- .../main.cpp | 104 ++++++++++++++++-- 1 file changed, 94 insertions(+), 10 deletions(-) diff --git a/test/performance/test.performance.platformstl.file_lines/main.cpp b/test/performance/test.performance.platformstl.file_lines/main.cpp index 7a2e752a..a9e53357 100644 --- a/test/performance/test.performance.platformstl.file_lines/main.cpp +++ b/test/performance/test.performance.platformstl.file_lines/main.cpp @@ -90,6 +90,13 @@ const std::size_t NUM_WARMUPS = 2; * helpers */ +enum line_ending_t +{ + line_ending_lf = 0 + , line_ending_crlf + , line_ending_cr +}; + std::string make_line( std::size_t length @@ -105,10 +112,31 @@ make_line( return line; } +char const* +line_ending_name_( + line_ending_t ending +) +{ + switch (ending) + { + case line_ending_cr: + + return "CR"; + case line_ending_crlf: + + return "CRLF"; + case line_ending_lf: + default: + + return "LF"; + } +} + bool write_test_file( - std::size_t num_lines -, std::size_t line_length + std::size_t num_lines +, std::size_t line_length +, line_ending_t ending ) { std::ofstream stm(TEST_FILE_NAME, std::ios::binary | std::ios::trunc); @@ -118,11 +146,32 @@ write_test_file( return false; } + char const* eol = "\n"; + std::size_t eol_len = 1u; + + switch (ending) + { + case line_ending_cr: + + eol = "\r"; + break; + case line_ending_crlf: + + eol = "\r\n"; + eol_len = 2u; + break; + case line_ending_lf: + default: + + break; + } + std::string const line = make_line(line_length); for (std::size_t i = 0; num_lines != i; ++i) { - stm << line << '\n'; + stm.write(line.data(), static_cast(line.size())); + stm.write(eol, static_cast(eol_len)); } return static_cast(stm); @@ -199,8 +248,9 @@ display_result( void run_scenario( - std::size_t num_lines -, std::size_t line_length + std::size_t num_lines +, std::size_t line_length +, line_ending_t ending ) { char scenario[64]; @@ -208,12 +258,14 @@ run_scenario( stlsoft::snprintf( scenario , sizeof(scenario) - , "%lu lines x %lu chars" + , "%lu %s x %lu %s" , static_cast(num_lines) + , (1u == num_lines) ? "line" : "lines" , static_cast(line_length) + , line_ending_name_(ending) ); - if (!write_test_file(num_lines, line_length)) + if (!write_test_file(num_lines, line_length, ending)) { std::cerr << "failed to write " @@ -225,7 +277,7 @@ run_scenario( } result_t const getline = time_(NUM_ITERATIONS, []() -> std::size_t { - std::ifstream stm(TEST_FILE_NAME); + std::ifstream stm(TEST_FILE_NAME, std::ios::binary); std::vector lines; std::string line; std::size_t anchor = 0; @@ -284,8 +336,40 @@ int main(int /*argc*/, char* /*argv*/[]) << std::endl ; - run_scenario(1000, 64); - run_scenario(5000, 80); + struct scenario_t + { + std::size_t num_lines; + std::size_t line_length; + line_ending_t ending; + }; + + /* LF, then CRLF, then CR. The two large shapes match the original LF + * runs. The small shapes (one line, a handful of lines, about 2KB) are + * the fixed-cost region a later small-file path has to beat. + */ + scenario_t const scenarios[] = + { + { 1000, 64, line_ending_lf, }, + { 1000, 64, line_ending_crlf, }, + { 1000, 64, line_ending_cr, }, + { 5000, 80, line_ending_lf, }, + { 5000, 80, line_ending_crlf, }, + { 5000, 80, line_ending_cr, }, + { 1, 16, line_ending_lf, }, + { 1, 16, line_ending_crlf, }, + { 1, 16, line_ending_cr, }, + { 8, 32, line_ending_lf, }, + { 8, 32, line_ending_crlf, }, + { 8, 32, line_ending_cr, }, + { 32, 64, line_ending_lf, }, + { 32, 64, line_ending_crlf, }, + { 32, 64, line_ending_cr, }, + }; + + { for (std::size_t i = 0; STLSOFT_NUM_ELEMENTS(scenarios) != i; ++i) + { + run_scenario(scenarios[i].num_lines, scenarios[i].line_length, scenarios[i].ending); + }} std::remove(TEST_FILE_NAME); From 1306104548c64b1ef6f190166f3322eae4ce66e9 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 19:03:18 +1000 Subject: [PATCH 09/12] refactor(platformstl): parse file_lines directly from mapping Remove the full-file m_contents heap copy; parse directly from the mmap'd base pointer for lower memory bandwidth use. Drop the unused m_contents member and early-return on empty maps. --- include/platformstl/filesystem/file_lines.hpp | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/include/platformstl/filesystem/file_lines.hpp b/include/platformstl/filesystem/file_lines.hpp index 079622f7..7a8c0022 100644 --- a/include/platformstl/filesystem/file_lines.hpp +++ b/include/platformstl/filesystem/file_lines.hpp @@ -48,8 +48,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_MAJOR 2 # define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_MINOR 1 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_REVISION 3 -# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_EDIT 54 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_REVISION 4 +# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_LINES_EDIT 55 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /** \file platformstl/filesystem/file_lines.hpp @@ -199,7 +199,6 @@ class basic_file_lines /// Creates an empty instance basic_file_lines() : m_mmf() - , m_contents() , m_strings() {} /// Creates an instance from the (contents of) the given path @@ -207,7 +206,6 @@ class basic_file_lines ss_explicit_k basic_file_lines(S const& path) : m_mmf() - , m_contents() , m_strings() { create_(path); @@ -220,7 +218,6 @@ class basic_file_lines /// Move the basic_file_lines(class_type&& rhs) STLSOFT_NOEXCEPT : m_mmf(std::move(rhs.m_mmf)) - , m_contents(std::move(rhs.m_contents)) , m_strings(std::move(rhs.m_strings)) {} #endif /* STLSOFT_CF_RVALUE_REFERENCES_SUPPORT */ @@ -392,13 +389,12 @@ class basic_file_lines size_t cch = static_cast(cb / sizeof(char_type)); #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */ - // 2. Create the contents string - - m_contents = base_string_type_(base, cch); - - STLSOFT_ASSERT(cch == m_contents.size()); + if (0u == cb) + { + return; + } - // 3. Parse the file, and populate the strings collection + // 2. Parse the file, and populate the strings collection size_t const numReserved = maximum(static_cast(128), 1u + (cch / 10u)); @@ -407,7 +403,7 @@ class basic_file_lines // This can work with EOL of CRLF or of LF, or a combination of the // two. - char_type const* const base1 = m_contents.data(); + char_type const* const base1 = base; char_type const* begin = base1; char_type const* const end = begin + cch; char_type const* s0 = begin; @@ -541,9 +537,8 @@ class basic_file_lines } private: // fields - HRW_Ref_type m_mmf; - base_string_type_ m_contents; - strings_type_ m_strings; + HRW_Ref_type m_mmf; + strings_type_ m_strings; }; From b726798d60bdfe603bce67c4206ed223f16f2c83 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 19:41:14 +1000 Subject: [PATCH 10/12] fix(platformstl): use C stdio in file_lines perf harness Avoid MinGW std::ifstream/std::getline crashes by switching the perf fixture writer/reader to fopen/fwrite/fgetc while preserving split on '\n' semantics (keeping '\r' in-line). --- .../main.cpp | 55 ++++++++++++++----- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/test/performance/test.performance.platformstl.file_lines/main.cpp b/test/performance/test.performance.platformstl.file_lines/main.cpp index a9e53357..16396c5a 100644 --- a/test/performance/test.performance.platformstl.file_lines/main.cpp +++ b/test/performance/test.performance.platformstl.file_lines/main.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -139,13 +138,6 @@ write_test_file( , line_ending_t ending ) { - std::ofstream stm(TEST_FILE_NAME, std::ios::binary | std::ios::trunc); - - if (!stm) - { - return false; - } - char const* eol = "\n"; std::size_t eol_len = 1u; @@ -168,13 +160,28 @@ write_test_file( std::string const line = make_line(line_length); + std::FILE* stm = std::fopen(TEST_FILE_NAME, "wb"); + + if (NULL == stm) + { + return false; + } + for (std::size_t i = 0; num_lines != i; ++i) { - stm.write(line.data(), static_cast(line.size())); - stm.write(eol, static_cast(eol_len)); + if (line.size() != std::fwrite(line.data(), 1u, line.size(), stm)) + { + std::fclose(stm); + return false; + } + if (eol_len != std::fwrite(eol, 1u, eol_len, stm)) + { + std::fclose(stm); + return false; + } } - return static_cast(stm); + return (0 == std::fclose(stm)); } template @@ -277,17 +284,39 @@ run_scenario( } result_t const getline = time_(NUM_ITERATIONS, []() -> std::size_t { - std::ifstream stm(TEST_FILE_NAME, std::ios::binary); std::vector lines; std::string line; std::size_t anchor = 0; - while (std::getline(stm, line)) + std::FILE* stm = std::fopen(TEST_FILE_NAME, "rb"); + + if (NULL == stm) + { + return 0u; + } + + for (int ch = std::fgetc(stm); EOF != ch; ch = std::fgetc(stm)) + { + if ('\n' == ch) + { + anchor += line.size(); + lines.push_back(line); + line.clear(); + } + else + { + line.push_back(static_cast(ch)); + } + } + + if (0u != line.size()) { anchor += line.size(); lines.push_back(line); } + std::fclose(stm); + return lines.size() + anchor; }); From 0763741f5a159fb8f06790d0ee174d9161cd3ea1 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Wed, 23 Sep 2026 20:17:26 +1000 Subject: [PATCH 11/12] fix(performance): use STLSOFT fopen wrapper in file_lines benchmark --- .../main.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/performance/test.performance.platformstl.file_lines/main.cpp b/test/performance/test.performance.platformstl.file_lines/main.cpp index 16396c5a..3dbc2375 100644 --- a/test/performance/test.performance.platformstl.file_lines/main.cpp +++ b/test/performance/test.performance.platformstl.file_lines/main.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -160,28 +161,28 @@ write_test_file( std::string const line = make_line(line_length); - std::FILE* stm = std::fopen(TEST_FILE_NAME, "wb"); + FILE* stm = NULL; - if (NULL == stm) + if (0 != STLSOFT_API_INTERNAL_stdio_fopen_m(TEST_FILE_NAME, "wb", &stm)) { return false; } for (std::size_t i = 0; num_lines != i; ++i) { - if (line.size() != std::fwrite(line.data(), 1u, line.size(), stm)) + if (line.size() != fwrite(line.data(), 1u, line.size(), stm)) { - std::fclose(stm); + fclose(stm); return false; } - if (eol_len != std::fwrite(eol, 1u, eol_len, stm)) + if (eol_len != fwrite(eol, 1u, eol_len, stm)) { - std::fclose(stm); + fclose(stm); return false; } } - return (0 == std::fclose(stm)); + return (0 == fclose(stm)); } template @@ -287,15 +288,14 @@ run_scenario( std::vector lines; std::string line; std::size_t anchor = 0; + FILE* stm = NULL; - std::FILE* stm = std::fopen(TEST_FILE_NAME, "rb"); - - if (NULL == stm) + if (0 != STLSOFT_API_INTERNAL_stdio_fopen_m(TEST_FILE_NAME, "rb", &stm)) { return 0u; } - for (int ch = std::fgetc(stm); EOF != ch; ch = std::fgetc(stm)) + for (int ch = fgetc(stm); EOF != ch; ch = fgetc(stm)) { if ('\n' == ch) { @@ -315,7 +315,7 @@ run_scenario( lines.push_back(line); } - std::fclose(stm); + fclose(stm); return lines.size() + anchor; }); From 0989edc0b7b8289ba2f42eb1e619570ea3e413de Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 24 Sep 2026 07:29:54 +1000 Subject: [PATCH 12/12] test(stlsoft): compare getline and getc file-line baselines - Rename the existing per-character stdio baseline to vector plus getc; - add a genuine std::getline baseline using the appropriate LF or CR delimiter and normalise CRLF line endings; --- .../main.cpp | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/performance/test.performance.platformstl.file_lines/main.cpp b/test/performance/test.performance.platformstl.file_lines/main.cpp index 3dbc2375..fe5c8916 100644 --- a/test/performance/test.performance.platformstl.file_lines/main.cpp +++ b/test/performance/test.performance.platformstl.file_lines/main.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -284,7 +285,7 @@ run_scenario( return; } - result_t const getline = time_(NUM_ITERATIONS, []() -> std::size_t { + result_t const getc = time_(NUM_ITERATIONS, []() -> std::size_t { std::vector lines; std::string line; std::size_t anchor = 0; @@ -320,6 +321,27 @@ run_scenario( return lines.size() + anchor; }); + result_t const getline = time_(NUM_ITERATIONS, [ending]() -> std::size_t { + std::ifstream stm(TEST_FILE_NAME, std::ios::binary); + std::vector lines; + std::string line; + std::size_t anchor = 0; + char const delimiter = (line_ending_cr == ending) ? '\r' : '\n'; + + while (std::getline(stm, line, delimiter)) + { + if (line_ending_crlf == ending && !line.empty() && '\r' == line.back()) + { + line.pop_back(); + } + + anchor += line.size(); + lines.push_back(line); + } + + return lines.size() + anchor; + }); + result_t const file_lines_std_string = time_(NUM_ITERATIONS, []() -> std::size_t { return read_file_lines_(); }); @@ -332,6 +354,7 @@ run_scenario( return read_file_lines_(); }); + display_result(scenario, "vector+getc", NUM_ITERATIONS, getc); display_result(scenario, "vector+getline", NUM_ITERATIONS, getline); display_result(scenario, "basic_file_lines", NUM_ITERATIONS, file_lines_std_string); display_result(scenario, "basic_file_lines", NUM_ITERATIONS, file_lines_stlsoft_simple_string);