@@ -59,30 +59,8 @@ static std::string escape_backslashes(const std::string &input) {
5959 return output;
6060}
6161
62- static void write_codspeed_benchmarks_to_json (
63- const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
62+ static std::string serialize_benchmark_objects (const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
6463 std::ostringstream oss;
65-
66- std::string creator_name = " codspeed-cpp" ;
67- std::string creator_version = CODSPEED_VERSION;
68- #ifdef _WIN32
69- auto creator_pid = _getpid ();
70- #else
71- pid_t creator_pid = getpid ();
72- #endif
73- std::string instrument_type = " walltime" ;
74-
75- oss << " {\n " ;
76- oss << " \" creator\" : {\n " ;
77- oss << " \" name\" : \" " << creator_name << " \" ,\n " ;
78- oss << " \" version\" : \" " << creator_version << " \" ,\n " ;
79- oss << " \" pid\" : " << creator_pid << " \n " ;
80- oss << " },\n " ;
81- oss << " \" instrument\" : {\n " ;
82- oss << " \" type\" : \" " << instrument_type << " \"\n " ;
83- oss << " },\n " ;
84- oss << " \" benchmarks\" : [\n " ;
85-
8664 for (size_t i = 0 ; i < benchmarks.size (); ++i) {
8765 const auto &benchmark = benchmarks[i];
8866 const auto &stats = benchmark.stats ;
@@ -118,40 +96,115 @@ static void write_codspeed_benchmarks_to_json(
11896 oss << " }" ;
11997
12098 if (i < benchmarks.size () - 1 ) {
121- oss << " ," ;
99+ oss << " ,\n " ;
122100 }
123- oss << " \n " ;
124101 }
102+ return oss.str ();
103+ }
125104
126- oss << " ]\n " ;
127- oss << " }" ;
128-
129- // Determine the directory path
105+ // Helper to resolve the correct target path
106+ static std::filesystem::path get_results_path () {
130107 std::string profile_folder = safe_getenv (" CODSPEED_PROFILE_FOLDER" );
131108 std::string directory = profile_folder.empty () ? " ." : profile_folder;
109+ return std::filesystem::path (directory) / " results" / " benchmarks.json" ;
110+ }
132111
133- // Create the results directory if it does not exist
134- std::filesystem::path results_path = directory + " /results" ;
135- if (!std::filesystem::exists (results_path)) {
136- if (!std::filesystem::create_directories (results_path)) {
137- std::cerr << " Failed to create directory: " << results_path << std::endl;
138- return ;
139- }
112+ static void write_codspeed_benchmarks_to_json (
113+ const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
114+
115+ std::filesystem::path file_path = get_results_path ();
116+ std::filesystem::create_directories (file_path.parent_path ());
117+
118+ std::ofstream out (file_path, std::ios::trunc);
119+ if (!out) {
120+ std::cerr << " Failed to open file for writing: " << file_path << std::endl;
121+ return ;
122+ }
123+
124+ std::string creator_name = " codspeed-cpp" ;
125+ std::string creator_version = CODSPEED_VERSION;
126+ #ifdef _WIN32
127+ auto creator_pid = _getpid ();
128+ #else
129+ pid_t creator_pid = getpid ();
130+ #endif
131+ std::string instrument_type = " walltime" ;
132+
133+ out << " {\n " ;
134+ out << " \" creator\" : {\n " ;
135+ out << " \" name\" : \" " << creator_name << " \" ,\n " ;
136+ out << " \" version\" : \" " << creator_version << " \" ,\n " ;
137+ out << " \" pid\" : " << creator_pid << " \n " ;
138+ out << " },\n " ;
139+ out << " \" instrument\" : {\n " ;
140+ out << " \" type\" : \" " << instrument_type << " \"\n " ;
141+ out << " },\n " ;
142+ out << " \" benchmarks\" : [\n " ;
143+
144+ out << serialize_benchmark_objects (benchmarks);
145+
146+ out << " \n ]\n }" ;
147+ }
148+
149+ static void append_codspeed_benchmarks_to_json (
150+ const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
151+
152+ if (benchmarks.empty ()) return ;
153+
154+ std::filesystem::path file_path = get_results_path ();
155+
156+ // Condition: If file is missing or entirely empty, generate fresh
157+ if (!std::filesystem::exists (file_path) || std::filesystem::file_size (file_path) == 0 ) {
158+ write_codspeed_benchmarks_to_json (benchmarks);
159+ return ;
140160 }
141161
142- // Create the file path
143- std::ostringstream file_path;
144- file_path << results_path.string () << " /" << creator_pid << " .json" ;
145-
146- // Write to file
147- std::ofstream out_file (file_path.str ());
148- if (out_file.is_open ()) {
149- out_file << oss.str ();
150- out_file.close ();
151- std::cout << " JSON written to " << file_path.str () << std::endl;
152- } else {
153- std::cerr << " Unable to open file " << file_path.str () << std::endl;
162+ // Open in read/write binary mode to prevent Windows newline conversion from messing with stream offsets
163+ std::fstream file (file_path, std::ios::in | std::ios::out | std::ios::binary);
164+ if (!file) {
165+ std::cerr << " Failed to open file for appending: " << file_path << std::endl;
166+ return ;
154167 }
168+
169+ // Back up from the EOF until we strip away '}', ']', and whitespace
170+ file.seekp (0 , std::ios::end);
171+ std::streampos pos = file.tellp ();
172+
173+ std::string closing_brackets;
174+ bool found_bracket = false ;
175+
176+ // Read backwards to safely find where the trailing array structural markers close
177+ while (pos > 0 ) {
178+ pos -= 1 ;
179+ file.seekg (pos);
180+ char ch;
181+ file.get (ch);
182+
183+ if (ch == ' }' || ch == ' ]' ) {
184+ found_bracket = true ;
185+ }
186+ // Keep tracking back until we cross into the array content zone
187+ if (found_bracket && ch != ' }' && ch != ' ]' && !std::isspace (static_cast <unsigned char >(ch))) {
188+ // Check if the last character before whitespace is the open bracket '[' (meaning array was empty)
189+ bool array_was_empty = (ch == ' [' );
190+
191+ // Move write pointer right after this text
192+ file.seekp (pos + std::streamoff (1 ));
193+
194+ if (!array_was_empty) {
195+ file << " ,\n " ; // Commas are mandatory between pre-existing elements
196+ } else {
197+ file << " \n " ;
198+ }
199+ break ;
200+ }
201+ }
202+
203+ // Inject the new items
204+ file << serialize_benchmark_objects (benchmarks);
205+
206+ // Re-close the JSON root safely
207+ file << " \n ]\n }" ;
155208}
156209
157210BenchmarkStats compute_benchmark_stats (
@@ -245,7 +298,7 @@ void generate_codspeed_walltime_report(
245298 codspeed_walltime_benchmarks.push_back (codspeed_benchmark);
246299 }
247300
248- write_codspeed_benchmarks_to_json (codspeed_walltime_benchmarks);
301+ append_codspeed_benchmarks_to_json (codspeed_walltime_benchmarks);
249302}
250303
251304} // namespace codspeed
0 commit comments