From 3bf2eb722e22418d4f40f62cd24e0930dfab7b4f Mon Sep 17 00:00:00 2001
From: Frank Winklmeier <frank.winklmeier@cern.ch>
Date: Wed, 25 Oct 2023 12:14:38 +0200
Subject: [PATCH] JobOptionSvc: use stringstream to read job options file

The current method of reading the input file leads to undefined behavior
errors in the ATLAS debug build. Use `std::stringstream` instead, which
is also supposedly faster.

Also rewrite `GetLastLineAndColumn` to not read the file twice.
---
 GaudiCoreSvc/src/JobOptionsSvc/Parser.cpp | 31 ++++++++++-------------
 1 file changed, 13 insertions(+), 18 deletions(-)

diff --git a/GaudiCoreSvc/src/JobOptionsSvc/Parser.cpp b/GaudiCoreSvc/src/JobOptionsSvc/Parser.cpp
index d62476dc91..24c920f3f9 100644
--- a/GaudiCoreSvc/src/JobOptionsSvc/Parser.cpp
+++ b/GaudiCoreSvc/src/JobOptionsSvc/Parser.cpp
@@ -1,5 +1,5 @@
 /***********************************************************************************\
-* (c) Copyright 1998-2020 CERN for the benefit of the LHCb and ATLAS collaborations *
+* (c) Copyright 1998-2023 CERN for the benefit of the LHCb and ATLAS collaborations *
 *                                                                                   *
 * This software is distributed under the terms of the Apache version 2 licence,     *
 * copied verbatim in the file "LICENSE".                                            *
@@ -19,6 +19,7 @@
 #include <boost/filesystem.hpp>
 #include <fmt/format.h>
 #include <fstream>
+#include <sstream>
 
 // ============================================================================
 namespace classic = boost::spirit::classic;
@@ -28,28 +29,23 @@ namespace gpu     = Gaudi::Parsers::Utils;
 namespace qi      = boost::spirit::qi;
 // ============================================================================
 namespace {
-  // ============================================================================
-  void GetLastLineAndColumn( std::ifstream& ifs, int& line, int& column ) {
-    int         n = 0;
-    std::string str;
-    while ( !ifs.eof() ) {
-      getline( ifs, str );
-      ++n;
+
+  // Return last line and column number of text in `s` with newline delimiter `delim`
+  std::pair<int, int> GetLastLineAndColumn( std::string_view s, const char delim = '\n' ) {
+    size_t line = 1;
+    for ( size_t p = s.find( delim ); p != s.npos; p = s.find( delim ) ) {
+      s.remove_prefix( p + 1 );
+      ++line;
     }
-    line   = n;
-    column = str.length() + 1;
-    ifs.clear();
-    ifs.seekg( 0, ifs.beg );
+    return { line, s.size() + 1 };
   }
 
   template <typename Grammar>
   bool ParseStream( std::ifstream& stream, const std::string& stream_name, gp::Messages* messages, gp::Node* root ) {
+    // Load input stream
+    const std::string input = ( std::ostringstream{} << stream.rdbuf() ).str();
 
-    int last_line, last_column;
-
-    GetLastLineAndColumn( stream, last_line, last_column );
-
-    std::string input( ( std::istreambuf_iterator<char>( stream ) ), std::istreambuf_iterator<char>() );
+    auto [last_line, last_column] = GetLastLineAndColumn( input );
 
     BaseIterator in_begin( input.begin() );
     // convert input iterator to forward iterator, usable by spirit parser
@@ -57,7 +53,6 @@ namespace {
     ForwardIterator fwd_end;
 
     // wrap forward iterator with position iterator, to record the position
-
     Iterator position_begin( fwd_begin, fwd_end, stream_name );
     Iterator position_end;
 
-- 
GitLab