LCOV - code coverage report
Current view: top level - src/extension - vcap_writer.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 568 570 99.6 %
Date: 2026-06-27 19:56:04 Functions: 31 32 96.9 %
Branches: 746 1231 60.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./extension/vcap_writer.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <cstdio>
      28                 :            : #include <filesystem>
      29                 :            : #include <fstream>
      30                 :            : #include <memory>
      31                 :            : #include <mutex>
      32                 :            : #include <optional>
      33                 :            : #include <string>
      34                 :            : #include <string_view>
      35                 :            : #include <unordered_map>
      36                 :            : #include <utility>
      37                 :            : #include <vector>
      38                 :            : 
      39                 :            : #include "./base/elapsed_timer.h"
      40                 :            : #include "./base/helpers.h"
      41                 :            : #include "./base/logger.h"
      42                 :            : #include "./version.h"
      43                 :            : 
      44                 :            : // json
      45                 :            : #include <nlohmann/json.hpp>
      46                 :            : 
      47                 :            : // mcap
      48                 :            : #include "./private/mcap_import.h"
      49                 :            : 
      50                 :            : // schema_plugin
      51                 :            : #include "./extension/schema_plugin_interface.h"
      52                 :            : 
      53                 :            : namespace vlink {
      54                 :            : 
      55                 :            : namespace {
      56                 :            : 
      57                 :        130 : std::string make_channel_key(const std::string& url, std::string_view action_name) {
      58                 :        130 :   std::string key = url;
      59         [ +  - ]:        130 :   key.push_back('\x1F');
      60         [ +  - ]:        130 :   key.append(action_name);
      61                 :        130 :   return key;
      62                 :            : }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      63                 :            : 
      64                 :            : }  // namespace
      65                 :            : 
      66                 :            : // VCAPWriter::Impl
      67                 :            : struct VCAPWriter::Impl final {  // NOLINT(clang-analyzer-optin.performance.Padding)
      68                 :            :   // UrlMsgInfo
      69                 :            :   struct UrlMsgInfo final {
      70                 :            :     int index{0};
      71                 :            :     size_t count{0};
      72                 :            :     size_t size{0};
      73                 :            :     int64_t first_timestamp{-1};
      74                 :            :     int64_t last_timestamp{-1};
      75                 :            :     double freq{0};
      76                 :            :     double loss{0};
      77                 :            :     std::string url;
      78                 :            :     std::string url_type;
      79                 :            :     std::string ser_type;
      80                 :            :     SchemaType schema_type{SchemaType::kUnknown};
      81                 :            :     ActionType action_type{ActionType::kUnknownAction};
      82                 :            : 
      83                 :         29 :     bool operator<(const UrlMsgInfo& target) const noexcept { return index < target.index; }
      84                 :            :   };
      85                 :            : 
      86                 :            :   std::atomic_bool is_dumping{false};
      87                 :            :   std::atomic_bool is_split_mode{false};
      88                 :            :   std::atomic<int> split_index{0};
      89                 :            :   std::atomic<int64_t> memory_size{0};
      90                 :            :   std::atomic_bool in_cached{false};
      91                 :            :   std::atomic<int64_t> cached_size{0};
      92                 :            :   std::atomic_bool quit_flag{false};
      93                 :            : 
      94                 :            :   std::string path;
      95                 :            :   std::string base_dir;
      96                 :            :   std::string base_name;
      97                 :            :   BagWriter::Config config;
      98                 :            :   ElapsedTimer elapsed_timer{ElapsedTimer::kMicro};
      99                 :            : 
     100                 :            :   int64_t current_row{0};
     101                 :            :   int64_t current_size{0};
     102                 :            :   bool has_oversize{false};
     103                 :            : 
     104                 :            :   int64_t last_timestamp{0};
     105                 :            : 
     106                 :            :   BagWriter::SystemClock time_start;
     107                 :            :   BagWriter::SystemClock time_current;
     108                 :            :   int64_t start_timestamp{0};
     109                 :            : 
     110                 :            :   std::vector<std::string> split_file_list;
     111                 :            :   bool split_before{false};
     112                 :            :   bool split_first{false};
     113                 :            : 
     114                 :            :   std::vector<std::string> total_url_list;
     115                 :            :   int64_t total_current_row{0};
     116                 :            :   int64_t total_current_size{0};
     117                 :            :   int64_t total_timestamp{0};
     118                 :            : 
     119                 :            :   std::unordered_map<std::string, UrlMsgInfo> url_map;
     120                 :            :   std::unordered_map<std::string, int> ser_map;
     121                 :            :   std::unordered_map<std::string, UrlMsgInfo> total_url_map;
     122                 :            :   std::unordered_map<std::string, SchemaData> total_schema_map;
     123                 :            : 
     124                 :            :   BagWriter::SplitCallback split_callback;
     125                 :            :   BagWriter::SchemaCallback schema_callback;
     126                 :            :   std::string split_filename;
     127                 :            :   std::mutex split_mtx;
     128                 :            : 
     129                 :            :   std::string app_name;
     130                 :            :   std::string tag_name;
     131                 :            :   int32_t timezone_diff{0};
     132                 :            : 
     133                 :            :   bool enable_compressed{false};
     134                 :            :   std::mutex write_mtx;
     135                 :            : 
     136                 :            :   std::string write_url_type;
     137                 :            : 
     138                 :            :   // mcap
     139                 :            :   std::optional<mcap::McapWriter> writer;
     140                 :            :   mcap::McapWriterOptions writer_options{"vlink"};
     141                 :            : 
     142                 :            :   // schema plugin interface
     143                 :            :   SchemaPluginInterface* schema_plugin_interface{nullptr};
     144                 :            : };
     145                 :            : 
     146                 :            : // VCAPWriter
     147                 :         64 : VCAPWriter::VCAPWriter(const std::string& path, const Config& config)
     148         [ +  - ]:         64 :     : BagWriter(path, config), impl_{std::make_unique<Impl>()} {
     149   [ +  -  +  - ]:         64 :   set_name("VCAPWriter");
     150                 :            : 
     151         [ +  - ]:         64 :   impl_->url_map.reserve(128);
     152         [ +  - ]:         64 :   impl_->ser_map.reserve(128);
     153   [ +  -  +  - ]:         64 :   url_loss_map_ref().reserve(128);
     154         [ +  - ]:         64 :   impl_->total_url_map.reserve(128);
     155   [ +  -  +  - ]:         64 :   total_url_loss_map_ref().reserve(128);
     156         [ +  - ]:         64 :   impl_->total_schema_map.reserve(128);
     157                 :            : 
     158         [ +  - ]:         64 :   impl_->schema_plugin_interface = get_schema_interface();
     159                 :            : 
     160   [ +  -  +  - ]:         64 :   impl_->app_name = get_default_app_name();
     161                 :            : 
     162         [ +  + ]:         64 :   if (config.tag_name.empty()) {
     163   [ +  -  +  - ]:          2 :     impl_->tag_name = get_default_tag_name();
     164                 :            :   } else {
     165         [ +  - ]:         62 :     impl_->tag_name = config.tag_name;
     166                 :            :   }
     167                 :            : 
     168         [ +  - ]:         64 :   impl_->timezone_diff = get_default_timezone_diff();
     169                 :            : 
     170         [ +  - ]:         64 :   impl_->path = path;
     171         [ +  - ]:         64 :   impl_->config = config;
     172                 :            : 
     173   [ +  +  +  + ]:         64 :   impl_->enable_compressed = impl_->config.compress == kCompressAuto || impl_->config.compress == kCompressZstd;
     174                 :            : 
     175         [ +  + ]:         64 :   if (impl_->enable_compressed) {
     176                 :            : #ifdef VLINK_ENABLE_ZSTD
     177                 :         13 :     impl_->writer_options.compression = mcap::Compression::Zstd;
     178                 :            : 
     179   [ +  +  +  +  :         13 :     switch (impl_->config.compress_level) {
                +  +  + ]
     180                 :          1 :       case 0:
     181                 :          1 :         impl_->writer_options.compressionLevel = mcap::CompressionLevel::Default;
     182                 :          1 :         break;
     183                 :          1 :       case 1:
     184                 :          1 :         impl_->writer_options.compressionLevel = mcap::CompressionLevel::Fastest;
     185                 :          1 :         break;
     186                 :          1 :       case 2:
     187                 :          1 :         impl_->writer_options.compressionLevel = mcap::CompressionLevel::Fast;
     188                 :          1 :         break;
     189                 :          3 :       case 3:
     190                 :          3 :         impl_->writer_options.compressionLevel = mcap::CompressionLevel::Default;
     191                 :          3 :         break;
     192                 :          3 :       case 4:
     193                 :          3 :         impl_->writer_options.compressionLevel = mcap::CompressionLevel::Slow;
     194                 :          3 :         break;
     195                 :          3 :       case 5:
     196                 :          3 :         impl_->writer_options.compressionLevel = mcap::CompressionLevel::Slowest;
     197                 :          3 :         break;
     198                 :          1 :       default:
     199                 :          1 :         impl_->writer_options.compressionLevel = mcap::CompressionLevel::Default;
     200                 :          1 :         break;
     201                 :            :     }
     202                 :            : #else
     203                 :            :     impl_->writer_options.compression = mcap::Compression::None;
     204                 :            :     impl_->config.compress = kCompressNone;
     205                 :            : 
     206                 :            :     impl_->enable_compressed = false;
     207                 :            :     VLOG_W("VCAPWriter: Compress is not supported.");
     208                 :            : #endif
     209                 :            :   } else {
     210                 :         51 :     impl_->writer_options.compression = mcap::Compression::None;
     211                 :            :   }
     212                 :            : 
     213         [ +  + ]:         64 :   if (impl_->config.cache_size > 0) {
     214                 :         62 :     impl_->writer_options.noChunking = false;
     215                 :         62 :     impl_->writer_options.chunkSize = impl_->config.cache_size;
     216                 :            :   } else {
     217                 :          2 :     impl_->writer_options.noChunking = true;
     218                 :          2 :     impl_->writer_options.chunkSize = 0;
     219                 :            : 
     220                 :          2 :     impl_->writer_options.compression = mcap::Compression::None;
     221                 :          2 :     impl_->config.compress = kCompressNone;
     222                 :            : 
     223         [ +  - ]:          2 :     if (impl_->enable_compressed) {
     224                 :          2 :       impl_->enable_compressed = false;
     225   [ +  -  +  - ]:          4 :       VLOG_W("VCAPWriter: Compress is not supported without cache_size > 0.");
     226                 :            :     }
     227                 :            :   }
     228                 :            : 
     229                 :            :   // if VUNLIKELY (impl_->config.wal_mode) {
     230                 :            :   //   VLOG_W("VCAPWriter not support [config.wal_mode]");
     231                 :            :   // }
     232                 :            : 
     233         [ +  + ]:         64 :   if VUNLIKELY (impl_->config.max_task_depth <= 0) {
     234                 :          1 :     impl_->config.max_task_depth = BagWriter::Config().max_task_depth;
     235                 :            :   }
     236                 :            : 
     237         [ +  - ]:         64 :   reset_lockfree_capacity();
     238                 :            : 
     239         [ +  + ]:         64 :   if VUNLIKELY (impl_->config.enable_limit) {
     240   [ +  -  +  - ]:          2 :     VLOG_W("VCAPWriter: Enable limit is not supported.");
     241                 :            :   }
     242                 :            : 
     243                 :            :   try {
     244                 :            : #ifdef _WIN32
     245                 :            :     std::filesystem::path file_path(Helpers::string_to_wstring(path));
     246                 :            :     std::string suffix = Helpers::path_to_string(file_path.extension());
     247                 :            : #else
     248         [ +  - ]:         64 :     std::filesystem::path file_path(path);
     249   [ +  -  +  - ]:         64 :     std::string suffix = file_path.extension().string();
     250                 :            : #endif
     251                 :            : 
     252                 :         64 :     std::filesystem::path parent_path;
     253                 :            : 
     254                 :            :     try {
     255         [ +  - ]:         64 :       parent_path = file_path.parent_path();
     256                 :            :     } catch (std::filesystem::filesystem_error&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     257                 :            :     }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     258                 :            : 
     259                 :        408 :     std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) { return std::tolower(c); });
     260                 :            : 
     261         [ +  + ]:         64 :     if (suffix == ".vcapx") {
     262   [ +  -  +  + ]:         24 :       if (std::filesystem::exists(file_path)) {
     263                 :            :         try {
     264                 :          1 :           nlohmann::json root_json;
     265                 :            : 
     266                 :            :           {
     267         [ +  - ]:          1 :             std::ifstream filex(file_path);
     268                 :            : 
     269         [ +  - ]:          1 :             filex >> root_json;
     270                 :            : 
     271         [ +  - ]:          1 :             filex.close();
     272                 :          1 :           }
     273                 :            : 
     274   [ +  -  +  - ]:          1 :           nlohmann::json files_json = root_json["VLinkFiles"];
     275                 :            : 
     276   [ +  -  +  -  :          2 :           for (const auto& file_info : files_json) {
             +  -  +  + ]
     277   [ +  -  +  -  :          1 :             if (!parent_path.empty() && std::filesystem::exists(parent_path / file_info)) {
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  -  -  
                      - ]
     278   [ +  -  +  -  :          1 :               std::filesystem::remove(parent_path / file_info);
                   +  - ]
     279                 :            :             }
     280                 :            :           }
     281                 :            : 
     282         [ +  - ]:          1 :           std::filesystem::remove(file_path);
     283         [ -  - ]:          1 :         } catch (nlohmann::json::exception&) {
     284                 :            :         }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     285                 :            :       }
     286                 :            : 
     287                 :         24 :       impl_->is_split_mode.store(true, std::memory_order_relaxed);
     288                 :         24 :       impl_->split_index.store(0, std::memory_order_relaxed);
     289                 :            : 
     290                 :            : #ifdef _WIN32
     291                 :            : 
     292                 :            :       if (parent_path.empty()) {
     293                 :            :         impl_->base_dir.clear();
     294                 :            :         impl_->base_name = Helpers::path_to_string(file_path.stem());
     295                 :            :       } else {
     296                 :            :         impl_->base_dir = Helpers::path_to_string(parent_path);
     297                 :            :         impl_->base_name = Helpers::path_to_string(std::filesystem::path(parent_path / file_path.stem()));
     298                 :            :       }
     299                 :            : #else
     300                 :            : 
     301         [ +  + ]:         24 :       if (parent_path.empty()) {
     302                 :          2 :         impl_->base_dir.clear();
     303   [ +  -  +  - ]:          2 :         impl_->base_name = file_path.stem().string();
     304                 :            :       } else {
     305         [ +  - ]:         22 :         impl_->base_dir = parent_path.string();
     306   [ +  -  +  -  :         22 :         impl_->base_name = std::filesystem::path(parent_path / file_path.stem()).string();
                   +  - ]
     307                 :            :       }
     308                 :            : #endif
     309                 :            : 
     310         [ +  - ]:         24 :       impl_->time_start = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
     311                 :         24 :       impl_->time_current = impl_->time_start;
     312                 :            : 
     313         [ +  + ]:         24 :       if (impl_->config.start_timestamp > 0) {
     314                 :         23 :         impl_->start_timestamp = impl_->config.start_timestamp;
     315                 :            :       } else {
     316                 :          1 :         impl_->start_timestamp = impl_->time_start.time_since_epoch().count();
     317                 :            :       }
     318                 :            : 
     319         [ +  - ]:         24 :       write_filex(false);
     320                 :            : 
     321         [ +  + ]:         24 :       if (impl_->config.split_name_by_time) {
     322         [ +  + ]:          3 :         if (impl_->base_dir.empty()) {
     323   [ +  -  +  - ]:          2 :           impl_->split_filename = get_format_date(&impl_->time_current, true) + ".vcap";
     324                 :            :         } else {
     325   [ +  -  +  -  :          1 :           impl_->split_filename = impl_->base_dir + "/" + get_format_date(&impl_->time_current, true) + ".vcap";
             +  -  +  - ]
     326                 :            :         }
     327                 :            :       } else {
     328                 :         21 :         impl_->split_filename =
     329   [ +  -  +  -  :         63 :             impl_->base_name + "." + std::to_string(impl_->split_index.load(std::memory_order_relaxed) + 1) + ".vcap";
             +  -  +  - ]
     330                 :            :       }
     331                 :            : 
     332         [ +  - ]:         24 :       open(impl_->split_filename);
     333                 :            :     } else {
     334         [ +  - ]:         40 :       impl_->time_start = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
     335                 :         40 :       impl_->time_current = impl_->time_start;
     336                 :            : 
     337         [ +  + ]:         40 :       if (impl_->config.start_timestamp > 0) {
     338                 :         37 :         impl_->start_timestamp = impl_->config.start_timestamp;
     339                 :            :       } else {
     340                 :          3 :         impl_->start_timestamp = impl_->time_start.time_since_epoch().count();
     341                 :            :       }
     342                 :            : 
     343                 :         40 :       impl_->is_split_mode.store(false, std::memory_order_relaxed);
     344                 :         40 :       impl_->split_index.store(0, std::memory_order_relaxed);
     345                 :            : 
     346         [ +  - ]:         40 :       open(path);
     347                 :            :     }
     348         [ -  - ]:         64 :   } catch (std::filesystem::filesystem_error& e) {
     349                 :            :     VLOG_F("VCAPWriter: Filesystem error, ", e.what(), ".");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     350                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     351                 :            : 
     352                 :         64 :   impl_->elapsed_timer.start();
     353                 :         64 : }
     354                 :            : 
     355                 :         66 : VCAPWriter::~VCAPWriter() {
     356                 :         64 :   detach_plugin();
     357                 :            : 
     358                 :         64 :   impl_->quit_flag.store(true, std::memory_order_release);
     359                 :            : 
     360         [ -  + ]:         64 :   if VUNLIKELY (!wait_for_idle(30000U)) {
     361                 :            :     VLOG_W("VCAPWriter: Force to quit.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     362                 :            :   }
     363                 :            : 
     364                 :         64 :   quit(true);
     365                 :            : 
     366                 :         64 :   wait_for_quit();
     367                 :            : 
     368                 :         64 :   close();
     369                 :            : 
     370         [ +  + ]:         64 :   if (impl_->is_split_mode.load(std::memory_order_relaxed)) {
     371                 :         24 :     write_filex(true);
     372                 :            :   }
     373                 :         66 : }
     374                 :            : 
     375                 :         22 : void VCAPWriter::register_split_callback(SplitCallback&& callback, bool before) {
     376         [ +  - ]:         22 :   std::lock_guard lock(impl_->split_mtx);
     377                 :         22 :   impl_->split_before = before;
     378                 :         22 :   impl_->split_callback = std::move(callback);
     379                 :         22 : }
     380                 :            : 
     381                 :          5 : void VCAPWriter::register_schema_callback(SchemaCallback&& callback) {
     382         [ +  - ]:          5 :   std::lock_guard lock(impl_->write_mtx);
     383                 :          5 :   impl_->schema_callback = std::move(callback);
     384                 :          5 : }
     385                 :            : 
     386                 :         18 : bool VCAPWriter::merge_schema(SchemaData& schema_data) {
     387                 :            :   const auto resolved_schema_type =
     388                 :         18 :       SchemaData::resolve_type(schema_data.schema_type, schema_data.name, schema_data.encoding);
     389                 :         18 :   schema_data.schema_type = resolved_schema_type;
     390                 :            : 
     391         [ +  + ]:         18 :   if VUNLIKELY (schema_data.name.empty()) {
     392                 :          1 :     return true;
     393                 :            :   }
     394                 :            : 
     395   [ +  +  +  +  :         17 :   if (schema_data.encoding.empty() && SchemaData::is_real_type(resolved_schema_type)) {
                   +  + ]
     396         [ +  - ]:          3 :     schema_data.encoding = std::string(SchemaData::convert_type(resolved_schema_type));
     397                 :            :   }
     398                 :            : 
     399         [ +  - ]:         17 :   std::string schema_key = schema_data.name;
     400         [ +  - ]:         17 :   schema_key.push_back('\x1F');
     401         [ +  - ]:         17 :   schema_key.append(SchemaData::convert_type(resolved_schema_type));
     402                 :            : 
     403                 :         17 :   std::string unknown_schema_key;
     404         [ +  - ]:         17 :   auto schema_iter = impl_->total_schema_map.find(schema_key);
     405                 :            : 
     406   [ +  +  +  +  :         17 :   if (schema_iter == impl_->total_schema_map.end() && SchemaData::is_real_type(resolved_schema_type)) {
                   +  + ]
     407         [ +  - ]:         13 :     unknown_schema_key = schema_data.name;
     408         [ +  - ]:         13 :     unknown_schema_key.push_back('\x1F');
     409         [ +  - ]:         13 :     schema_iter = impl_->total_schema_map.find(unknown_schema_key);
     410                 :            :   }
     411                 :            : 
     412         [ +  + ]:         17 :   if (schema_iter == impl_->total_schema_map.end()) {
     413         [ +  - ]:         13 :     impl_->total_schema_map.emplace(schema_key, schema_data);
     414                 :            :   } else {
     415                 :          4 :     auto& current = schema_iter->second;
     416                 :            : 
     417   [ +  -  +  +  :          4 :     if VUNLIKELY ((!schema_data.encoding.empty() && !current.encoding.empty() &&
          +  +  -  +  +  
          -  +  -  +  +  
          +  +  +  +  +  
          +  +  +  +  -  
          +  +  +  +  -  
             +  -  +  +  
                      + ]
     418                 :            :                    current.encoding != schema_data.encoding) ||
     419                 :            :                   (!schema_data.data.empty() && !current.data.empty() && current.data != schema_data.data) ||
     420                 :            :                   (SchemaData::is_real_type(resolved_schema_type) && SchemaData::is_real_type(current.schema_type) &&
     421                 :            :                    current.schema_type != resolved_schema_type)) {
     422   [ +  -  +  - ]:          4 :       CLOG_E("VCAPWriter: Conflicting schema pushed for [%s].", schema_data.name.c_str());
     423                 :          2 :       return false;
     424                 :            :     }
     425                 :            : 
     426   [ +  +  +  -  :          2 :     if (current.encoding.empty() && !schema_data.encoding.empty()) {
                   +  + ]
     427         [ +  - ]:          1 :       current.encoding = schema_data.encoding;
     428                 :            :     }
     429                 :            : 
     430   [ +  +  +  -  :          2 :     if (current.data.empty() && !schema_data.data.empty()) {
                   +  + ]
     431                 :          1 :       current.data = schema_data.data;
     432                 :            :     }
     433                 :            : 
     434   [ +  +  +  -  :          2 :     if (!SchemaData::is_real_type(current.schema_type) && SchemaData::is_real_type(resolved_schema_type)) {
                   +  + ]
     435                 :          1 :       current.schema_type = resolved_schema_type;
     436                 :            :     }
     437                 :            : 
     438         [ +  - ]:          2 :     schema_data = current;
     439                 :            : 
     440   [ +  +  +  -  :          2 :     if (schema_iter->first != schema_key && current.schema_type == resolved_schema_type) {
                   +  + ]
     441         [ +  - ]:          1 :       impl_->total_schema_map.erase(schema_iter);
     442         [ +  - ]:          1 :       schema_iter = impl_->total_schema_map.emplace(schema_key, schema_data).first;
     443                 :            :     }
     444                 :            :   }
     445                 :            : 
     446                 :         15 :   return true;
     447                 :         17 : }
     448                 :            : 
     449                 :        127 : bool VCAPWriter::load_schema(const std::string& ser_type, SchemaType& schema_type, SchemaData& schema_data) {
     450                 :        127 :   schema_data = SchemaData{};
     451                 :            : 
     452         [ -  + ]:        127 :   if VUNLIKELY (ser_type.empty()) {
     453                 :            :     return true;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     454                 :            :   }
     455                 :            : 
     456         [ +  - ]:        127 :   std::string schema_key = ser_type;
     457         [ +  - ]:        127 :   schema_key.push_back('\x1F');
     458         [ +  - ]:        127 :   schema_key.append(SchemaData::convert_type(schema_type));
     459                 :            : 
     460                 :        127 :   std::string unknown_schema_key;
     461                 :        127 :   auto schema_iter = impl_->total_schema_map.end();
     462                 :            : 
     463         [ +  + ]:        127 :   if (schema_type != SchemaType::kUnknown) {
     464         [ +  - ]:        118 :     schema_iter = impl_->total_schema_map.find(schema_key);
     465                 :            : 
     466         [ +  + ]:        118 :     if (schema_iter == impl_->total_schema_map.end()) {
     467         [ +  - ]:        102 :       unknown_schema_key = ser_type;
     468         [ +  - ]:        102 :       unknown_schema_key.push_back('\x1F');
     469         [ +  - ]:        102 :       schema_iter = impl_->total_schema_map.find(unknown_schema_key);
     470                 :            :     }
     471                 :            :   } else {
     472   [ +  -  +  - ]:         18 :     const auto prefix = ser_type + std::string("\x1F");
     473                 :            : 
     474         [ +  + ]:         18 :     for (auto iter = impl_->total_schema_map.begin(); iter != impl_->total_schema_map.end(); ++iter) {
     475         [ +  + ]:          9 :       if (!Helpers::has_startwith(iter->first, prefix)) {
     476                 :          8 :         continue;
     477                 :            :       }
     478                 :            : 
     479         [ -  + ]:          1 :       if (schema_iter != impl_->total_schema_map.end()) {
     480                 :            :         schema_iter = impl_->total_schema_map.end();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     481                 :            :         break;                                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     482                 :            :       }
     483                 :            : 
     484                 :          1 :       schema_iter = iter;
     485                 :            :     }
     486                 :          9 :   }
     487                 :            : 
     488         [ +  + ]:        127 :   if (schema_iter != impl_->total_schema_map.end()) {
     489         [ +  - ]:         17 :     schema_data = schema_iter->second;
     490         [ -  + ]:        110 :   } else if (impl_->schema_plugin_interface) {
     491                 :            :     schema_data =
     492                 :            :         impl_->schema_plugin_interface->search_schema(ser_type, schema_type);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     493         [ +  + ]:        110 :   } else if (impl_->schema_callback) {
     494         [ +  - ]:          8 :     schema_data = impl_->schema_callback(ser_type, schema_type);
     495                 :            :   }
     496                 :            : 
     497                 :        127 :   schema_type = SchemaData::resolve_type(schema_type, ser_type, schema_data.encoding);
     498                 :        127 :   schema_data.schema_type = SchemaData::resolve_type(schema_data.schema_type, ser_type, schema_data.encoding);
     499                 :            : 
     500   [ +  +  +  - ]:        127 :   if (schema_type != SchemaType::kUnknown && schema_data.schema_type != SchemaType::kUnknown &&
     501         [ +  + ]:        119 :       schema_type != schema_data.schema_type) {
     502   [ +  -  +  - ]:          4 :     CLOG_E("VCAPWriter: Schema family mismatch for [%s], requested = %d, resolved = %d.", ser_type.c_str(),
     503                 :            :            static_cast<int>(schema_type), static_cast<int>(schema_data.schema_type));
     504                 :          2 :     return false;
     505                 :            :   }
     506                 :            : 
     507   [ +  +  +  +  :        125 :   if (schema_type != SchemaType::kUnknown && schema_data.encoding.empty()) {
                   +  + ]
     508         [ +  - ]:         93 :     schema_data.encoding = std::string(SchemaData::convert_type(schema_type));
     509                 :            :   }
     510                 :            : 
     511   [ +  +  -  + ]:        125 :   if (schema_data.schema_type == SchemaType::kUnknown && schema_type != SchemaType::kUnknown) {
     512                 :            :     schema_data.schema_type = schema_type;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     513                 :            :   }
     514                 :            : 
     515         [ +  + ]:        125 :   if (!schema_data.name.empty()) {
     516         [ +  - ]:         24 :     std::string resolved_schema_key = ser_type;
     517         [ +  - ]:         24 :     resolved_schema_key.push_back('\x1F');
     518         [ +  - ]:         24 :     resolved_schema_key.append(SchemaData::convert_type(schema_data.schema_type));
     519                 :            : 
     520         [ -  + ]:         41 :     if (schema_iter != impl_->total_schema_map.end() && schema_iter->first != resolved_schema_key &&
     521   [ +  +  -  -  :         41 :         schema_iter->second.schema_type == SchemaType::kUnknown && schema_data.schema_type != SchemaType::kUnknown) {
             -  -  -  + ]
     522                 :            :       impl_->total_schema_map.erase(schema_iter);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     523                 :            :     }
     524                 :            : 
     525         [ +  - ]:         24 :     impl_->total_schema_map.insert_or_assign(resolved_schema_key, schema_data);
     526                 :         24 :   }
     527                 :            : 
     528                 :        125 :   return true;
     529                 :        127 : }
     530                 :            : 
     531                 :         18 : bool VCAPWriter::push_schema(const SchemaData& schema_data, bool immediate) {
     532         [ +  - ]:         18 :   SchemaData stored_schema = schema_data;
     533                 :            : 
     534         [ +  + ]:         18 :   if VUNLIKELY (!stored_schema.data.is_owner()) {
     535                 :          2 :     stored_schema.data.deep_copy(schema_data.data);
     536                 :            :   }
     537                 :            : 
     538         [ +  + ]:         18 :   if (immediate) {
     539         [ +  - ]:         16 :     std::lock_guard lock(impl_->write_mtx);
     540         [ +  - ]:         16 :     return merge_schema(stored_schema);
     541                 :         16 :   }
     542                 :            : 
     543   [ +  -  +  - ]:          2 :   bool posted = post_task([this, stored_schema = std::move(stored_schema)]() mutable {
     544         [ +  - ]:          2 :     std::lock_guard lock(impl_->write_mtx);
     545                 :            : 
     546   [ +  -  -  + ]:          2 :     if VUNLIKELY (!merge_schema(stored_schema)) {
     547   [ #  #  #  # ]:          0 :       CLOG_E("VCAPWriter: Deferred merge_schema failed for [%s] in async push_schema path.",
     548                 :            :              stored_schema.name.c_str());
     549                 :            :     }
     550                 :          2 :   });
     551                 :            : 
     552                 :          2 :   return posted;
     553                 :         18 : }
     554                 :            : 
     555                 :        131 : int64_t VCAPWriter::record(const Frame& frame, bool immediate) {
     556                 :        131 :   const std::string& url = frame.url;
     557                 :        131 :   const std::string& ser_type = frame.ser_type;
     558                 :        131 :   const SchemaType schema_type = frame.schema_type;
     559                 :        131 :   const ActionType action_type = frame.action_type;
     560                 :        131 :   const Bytes& data = frame.data;
     561                 :        131 :   const int64_t microseconds_timestamp = frame.timestamp;
     562                 :            : 
     563         [ +  + ]:        131 :   if (immediate) {
     564         [ +  - ]:        125 :     std::lock_guard lock(impl_->write_mtx);
     565                 :            : 
     566   [ +  -  +  + ]:        125 :     if VUNLIKELY (!write(url, ser_type, schema_type, action_type, data, microseconds_timestamp)) {
     567                 :          4 :       return -1;
     568                 :            :     }
     569         [ +  + ]:        125 :   } else {
     570         [ +  + ]:         12 :     if VUNLIKELY (impl_->memory_size.load(std::memory_order_relaxed) + static_cast<int64_t>(data.size()) >
     571                 :            :                   impl_->config.max_memory_size) {
     572   [ +  -  +  - ]:          2 :       CLOG_E("The memory data in the queue exceeds %.1fGB and the task is automatically discarded.",
     573                 :            :              impl_->config.max_memory_size / 1024.0 / 1024.0 / 1024.0);
     574                 :            : 
     575                 :          1 :       return -1;
     576                 :            :     }
     577                 :            : 
     578                 :          5 :     int url_index = -1;
     579                 :          5 :     int ser_index = -1;
     580                 :            : 
     581         [ +  - ]:          5 :     get_url_meta(url, ser_type, url_index, ser_index);
     582                 :            : 
     583                 :          5 :     const auto queued_size = data.size();
     584                 :            : 
     585                 :          5 :     impl_->memory_size.fetch_add(queued_size, std::memory_order_relaxed);
     586                 :            : 
     587   [ +  -  +  - ]:          5 :     bool posted = post_task([this, url_index, ser_index, schema_type, action_type, data, queued_size,
     588                 :            :                              microseconds_timestamp]() {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     589         [ +  - ]:          5 :       std::lock_guard lock(impl_->write_mtx);
     590                 :          5 :       std::string url;
     591                 :          5 :       std::string ser_type;
     592                 :            : 
     593         [ +  - ]:          5 :       get_url_meta(url_index, ser_index, url, ser_type);
     594                 :            : 
     595         [ +  - ]:          5 :       write(url, ser_type, schema_type, action_type, data, microseconds_timestamp);
     596                 :            : 
     597                 :          5 :       impl_->memory_size.fetch_sub(queued_size, std::memory_order_relaxed);
     598                 :          5 :     });
     599                 :            : 
     600         [ -  + ]:          5 :     if VUNLIKELY (!posted) {
     601                 :            :       impl_->memory_size.fetch_sub(queued_size, std::memory_order_relaxed);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     602                 :            :       return -1;                                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     603                 :            :     }
     604                 :            :   }
     605                 :            : 
     606                 :        126 :   return microseconds_timestamp;
     607                 :            : }
     608                 :            : 
     609                 :          1 : int64_t VCAPWriter::get_record_timestamp() const { return impl_->elapsed_timer.get(); }
     610                 :            : 
     611                 :          8 : bool VCAPWriter::is_dumping() const { return impl_->is_dumping.load(std::memory_order_relaxed); }
     612                 :            : 
     613                 :         31 : bool VCAPWriter::is_split_mode() const { return impl_->is_split_mode.load(std::memory_order_relaxed); }
     614                 :            : 
     615                 :         88 : int VCAPWriter::get_split_index() const { return impl_->split_index.load(std::memory_order_relaxed); }
     616                 :            : 
     617                 :          8 : size_t VCAPWriter::get_max_task_count() const { return impl_->config.max_task_depth; }
     618                 :            : 
     619                 :          5 : void VCAPWriter::on_begin() {
     620                 :          5 :   MessageLoop::on_begin();
     621                 :            : 
     622                 :          5 :   impl_->elapsed_timer.restart();
     623                 :          5 : }
     624                 :            : 
     625                 :          5 : void VCAPWriter::on_end() { MessageLoop::on_end(); }
     626                 :            : 
     627                 :         89 : void VCAPWriter::open(const std::string& path) {
     628                 :            :   try {
     629                 :            : #ifdef _WIN32
     630                 :            :     impl_->split_file_list.emplace_back(Helpers::path_to_string(std::filesystem::path(path).filename()));
     631                 :            :     std::filesystem::path file_path(Helpers::string_to_wstring(path));
     632                 :            : #else
     633   [ +  -  +  -  :         89 :     impl_->split_file_list.emplace_back(std::filesystem::path(path).filename().string());
             +  -  +  - ]
     634         [ +  - ]:         89 :     std::filesystem::path file_path(path);
     635                 :            : #endif
     636                 :            : 
     637   [ +  -  +  + ]:         89 :     if (std::filesystem::exists(file_path)) {
     638         [ +  - ]:          1 :       std::filesystem::remove(file_path);
     639                 :            :     } else {
     640         [ +  - ]:         88 :       auto parent_path = file_path.parent_path();
     641                 :            : 
     642   [ +  +  +  -  :         88 :       if (!parent_path.empty() && !std::filesystem::exists(parent_path)) {
             +  +  +  + ]
     643         [ +  - ]:          1 :         std::filesystem::create_directories(parent_path);
     644                 :            :       }
     645                 :         88 :     }
     646         [ -  - ]:         89 :   } catch (std::filesystem::filesystem_error& e) {
     647                 :            :     VLOG_F("VCAPWriter: Filesystem error, ", e.what(), ".");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     648                 :            :     return;                                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     649                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     650                 :            : 
     651                 :         89 :   mcap::Status status;
     652                 :            : 
     653         [ +  - ]:         89 :   impl_->writer.emplace();
     654                 :            : 
     655         [ +  - ]:         89 :   status = impl_->writer->open(path, impl_->writer_options);
     656                 :            : 
     657         [ -  + ]:         89 :   if VUNLIKELY (!status.ok()) {
     658                 :            :     CLOG_F("VCAPWriter: Failed to open vcap, error = %s.", status.message.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     659                 :            :     return;                                                                          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     660                 :            :   }
     661                 :            : 
     662                 :         89 :   mcap::Metadata header_meta_data;
     663         [ +  - ]:         89 :   header_meta_data.name = "VLinkHeader";
     664   [ +  -  +  -  :         89 :   header_meta_data.metadata["tag"] = impl_->tag_name;
                   +  - ]
     665   [ +  -  +  -  :         89 :   header_meta_data.metadata["version"] = VLINK_VERSION;
                   +  - ]
     666   [ +  +  +  -  :         89 :   header_meta_data.metadata["compress"] = impl_->enable_compressed ? "zstd" : "None";
             +  -  +  - ]
     667   [ +  -  +  -  :         89 :   header_meta_data.metadata["process"] = impl_->app_name;
                   +  - ]
     668   [ +  -  +  -  :         89 :   header_meta_data.metadata["date"] = get_format_date(&impl_->time_current);
                   +  - ]
     669   [ +  -  +  -  :         89 :   header_meta_data.metadata["timezone"] = std::to_string(impl_->timezone_diff);
                   +  - ]
     670   [ +  -  +  -  :         89 :   header_meta_data.metadata["start_timestamp"] = std::to_string(impl_->start_timestamp);
                   +  - ]
     671                 :            : 
     672         [ +  - ]:         89 :   status = impl_->writer->write(header_meta_data);
     673                 :            : 
     674         [ -  + ]:         89 :   if VUNLIKELY (!status.ok()) {
     675                 :            :     CLOG_F("VCAPWriter: Failed to write header meta data, error = %s.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     676                 :            :            status.message.c_str());                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     677                 :            :     return;                                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     678                 :            :   }
     679                 :            : 
     680                 :         89 :   impl_->last_timestamp = 0;
     681   [ +  -  +  - ]:         89 : }
     682                 :            : 
     683                 :         89 : void VCAPWriter::close() {
     684         [ -  + ]:         89 :   if VUNLIKELY (!impl_->writer) {
     685                 :            :     VLOG_E("VCAPWriter: Writer is not open.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     686                 :            :     return;                                     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     687                 :            :   }
     688                 :            : 
     689                 :         89 :   mcap::Status status;
     690                 :            : 
     691                 :         89 :   std::vector<Impl::UrlMsgInfo> msg_info_list;
     692         [ +  - ]:         89 :   msg_info_list.reserve(impl_->url_map.size());
     693                 :            : 
     694                 :            :   {
     695   [ +  -  +  - ]:         89 :     std::lock_guard lock(sample_mutex());
     696                 :            : 
     697         [ +  + ]:        201 :     for (const auto& entry : impl_->url_map) {
     698                 :        112 :       const auto& msg_info = entry.second;
     699         [ +  - ]:        112 :       msg_info_list.emplace_back(msg_info);
     700                 :            : 
     701                 :        112 :       auto& last = msg_info_list.back();
     702   [ +  -  +  -  :        112 :       auto loss_iter = url_loss_map_ref().find(recover_recorded_url(last.url));
                   +  - ]
     703   [ +  -  +  + ]:        112 :       last.loss = loss_iter == url_loss_map_ref().end() ? 0 : loss_iter->second;
     704                 :            :     }
     705                 :         89 :   }
     706                 :            : 
     707         [ +  - ]:         89 :   std::sort(msg_info_list.begin(), msg_info_list.end());
     708                 :            : 
     709         [ +  + ]:        201 :   for (const auto& msg_info : msg_info_list) {
     710                 :        112 :     mcap::Metadata channel_meta_data;
     711   [ +  -  +  - ]:        112 :     channel_meta_data.name = "VLinkChannel_" + std::to_string(msg_info.index + 1);
     712   [ +  -  +  -  :        112 :     channel_meta_data.metadata["index"] = std::to_string(msg_info.index);
                   +  - ]
     713   [ +  -  +  -  :        112 :     channel_meta_data.metadata["type"] = msg_info.url_type;
                   +  - ]
     714   [ +  -  +  -  :        112 :     channel_meta_data.metadata["action"] = std::string(convert_action(msg_info.action_type));
             +  -  +  - ]
     715   [ +  -  +  -  :        112 :     channel_meta_data.metadata["encoding"] = std::string(SchemaData::convert_type(msg_info.schema_type));
                   +  - ]
     716   [ +  -  +  -  :        112 :     channel_meta_data.metadata["ser"] = msg_info.ser_type;
                   +  - ]
     717   [ +  -  +  -  :        112 :     channel_meta_data.metadata["count"] = std::to_string(msg_info.count);
                   +  - ]
     718   [ +  -  +  -  :        112 :     channel_meta_data.metadata["size"] = std::to_string(msg_info.size);
                   +  - ]
     719   [ +  -  +  - ]:        112 :     channel_meta_data.metadata["loss"] = Helpers::double_to_string(msg_info.loss, 6);
     720   [ +  -  +  -  :        112 :     channel_meta_data.metadata["freq"] = std::to_string(msg_info.freq);
                   +  - ]
     721                 :            : 
     722         [ +  - ]:        112 :     status = impl_->writer->write(channel_meta_data);
     723                 :            : 
     724         [ -  + ]:        112 :     if VUNLIKELY (!status.ok()) {
     725                 :            :       CLOG_F("VCAPWriter: Failed to write channel meta data, error = %s.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     726                 :            :              status.message.c_str());                                       // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     727                 :            :       return;                                                               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     728                 :            :     }
     729         [ +  - ]:        112 :   }
     730                 :            : 
     731         [ +  - ]:         89 :   impl_->writer->close();
     732         [ +  - ]:         89 :   impl_->writer->terminate();
     733                 :         89 :   impl_->writer.reset();
     734                 :            : 
     735                 :         89 :   impl_->url_map.clear();
     736                 :         89 :   impl_->ser_map.clear();
     737         [ +  - ]:         89 :   url_loss_map_ref().clear();
     738                 :            : 
     739                 :         89 :   impl_->current_row = 0;
     740                 :         89 :   impl_->current_size = 0;
     741                 :         89 :   impl_->has_oversize = false;
     742                 :            : 
     743                 :         89 :   impl_->in_cached.store(false, std::memory_order_relaxed);
     744                 :         89 :   impl_->cached_size.store(0, std::memory_order_relaxed);
     745   [ +  -  +  - ]:         89 : }
     746                 :            : 
     747                 :        130 : bool VCAPWriter::write(const std::string& url, const std::string& ser_type, SchemaType schema_type,
     748                 :            :                        ActionType action_type, const Bytes& data, int64_t microseconds_timestamp) {
     749         [ -  + ]:        130 :   if VUNLIKELY (!impl_->writer) {
     750                 :            :     VLOG_E("VCAPWriter: Writer is not open.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     751                 :            :     return false;                               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     752                 :            :   }
     753                 :            : 
     754   [ +  +  +  +  :        130 :   if VUNLIKELY (impl_->is_split_mode.load(std::memory_order_relaxed) && !impl_->split_first) {
                   +  + ]
     755                 :         23 :     impl_->split_first = true;
     756                 :            : 
     757         [ +  - ]:         23 :     std::lock_guard split_lock(impl_->split_mtx);
     758                 :            : 
     759   [ +  +  +  +  :         44 :     if (!impl_->split_before && impl_->split_callback && impl_->split_index.load(std::memory_order_relaxed) == 0) {
             +  -  +  + ]
     760         [ +  - ]:         21 :       impl_->split_callback(0, impl_->split_filename);
     761                 :            :     }
     762                 :         23 :   }
     763                 :            : 
     764                 :        130 :   mcap::Status status;
     765                 :            : 
     766                 :        130 :   bool do_split = false;
     767                 :            : 
     768                 :            :   // split
     769                 :            : 
     770   [ +  +  +  +  :        130 :   if (impl_->is_split_mode.load(std::memory_order_relaxed) && !impl_->url_map.empty()) {
                   +  + ]
     771   [ +  +  +  + ]:         29 :     if (impl_->config.split_by_time > 0 &&
     772                 :          4 :         (microseconds_timestamp - impl_->config.begin_time * 1000) >
     773         [ +  - ]:          4 :             impl_->config.split_by_time * 1000 * static_cast<int64_t>(impl_->split_file_list.size())) {
     774                 :          4 :       do_split = true;
     775   [ +  -  +  -  :         42 :     } else if (impl_->config.split_by_time <= 0 && impl_->config.split_by_size > 0 &&
                   +  - ]
     776         [ +  - ]:         21 :                (impl_->current_size + static_cast<int64_t>(data.size())) > impl_->config.split_by_size) {
     777                 :         21 :       do_split = true;
     778                 :            :     } else {
     779                 :          0 :       do_split = false;
     780                 :            :     }
     781                 :            : 
     782         [ +  - ]:         25 :     if VUNLIKELY (do_split) {
     783         [ +  - ]:         25 :       std::lock_guard split_lock(impl_->split_mtx);
     784                 :            : 
     785                 :         25 :       impl_->split_index.fetch_add(1, std::memory_order_relaxed);
     786         [ +  - ]:         25 :       impl_->time_current = impl_->time_start + std::chrono::milliseconds(microseconds_timestamp / 1000U);
     787                 :            : 
     788         [ +  + ]:         25 :       if (impl_->config.split_name_by_time) {
     789         [ +  + ]:          4 :         if (impl_->base_dir.empty()) {
     790   [ +  -  +  - ]:          2 :           impl_->split_filename = get_format_date(&impl_->time_current, true) + ".vcap";
     791                 :            :         } else {
     792   [ +  -  +  -  :          2 :           impl_->split_filename = impl_->base_dir + "/" + get_format_date(&impl_->time_current, true) + ".vcap";
             +  -  +  - ]
     793                 :            :         }
     794                 :            :       } else {
     795                 :         21 :         impl_->split_filename =
     796   [ +  -  +  -  :         63 :             impl_->base_name + "." + std::to_string(impl_->split_index.load(std::memory_order_relaxed) + 1) + ".vcap";
             +  -  +  - ]
     797                 :            :       }
     798                 :            : 
     799   [ +  +  +  -  :         25 :       if (impl_->split_before && impl_->split_callback) {
                   +  + ]
     800         [ +  - ]:          2 :         impl_->split_callback(impl_->split_index.load(std::memory_order_relaxed), impl_->split_filename);
     801                 :            :       }
     802                 :            : 
     803         [ +  - ]:         25 :       close();
     804                 :            : 
     805         [ +  - ]:         25 :       write_filex(false);
     806                 :            : 
     807         [ +  - ]:         25 :       open(impl_->split_filename);
     808                 :            : 
     809   [ +  +  +  +  :         25 :       if (!impl_->split_before && impl_->split_callback) {
                   +  + ]
     810         [ +  - ]:         46 :         impl_->split_callback(impl_->split_index.load(std::memory_order_relaxed), impl_->split_filename);
     811                 :            :       }
     812                 :         25 :     }
     813                 :            :   }
     814                 :            : 
     815                 :            :   // insert url
     816   [ +  -  +  - ]:        130 :   const std::string channel_key = make_channel_key(url, convert_action(action_type));
     817         [ +  - ]:        130 :   auto total_url_iter_ret = impl_->total_url_map.try_emplace(channel_key, Impl::UrlMsgInfo());
     818         [ +  - ]:        130 :   auto url_iter_ret = impl_->url_map.try_emplace(channel_key, Impl::UrlMsgInfo());
     819                 :            : 
     820                 :        130 :   Impl::UrlMsgInfo& total_url_msg_info = total_url_iter_ret.first->second;
     821                 :            : 
     822                 :        130 :   Impl::UrlMsgInfo& url_msg_info = url_iter_ret.first->second;
     823                 :        130 :   auto resolved_schema_type = SchemaData::resolve_type(schema_type, ser_type);
     824         [ +  - ]:        130 :   std::string next_ser_type = total_url_msg_info.ser_type;
     825                 :        130 :   SchemaType next_schema_type = total_url_msg_info.schema_type;
     826                 :            : 
     827         [ +  + ]:        130 :   if (total_url_iter_ret.second) {
     828         [ +  - ]:         88 :     next_ser_type = ser_type;
     829                 :         88 :     next_schema_type = resolved_schema_type;
     830                 :            :   } else {
     831         [ +  - ]:         42 :     if (!ser_type.empty()) {
     832         [ -  + ]:         42 :       if (next_ser_type.empty()) {
     833                 :            :         next_ser_type = ser_type;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     834         [ +  + ]:         42 :       } else if VUNLIKELY (next_ser_type != ser_type) {
     835   [ +  -  +  - ]:          4 :         CLOG_E("VCAPWriter: URL [%s] ser changed from [%s] to [%s].", url.c_str(), next_ser_type.c_str(),
     836                 :            :                ser_type.c_str());
     837                 :          2 :         return false;
     838                 :            :       }
     839                 :            :     }
     840                 :            :   }
     841                 :            : 
     842                 :        128 :   SchemaData schema_data;
     843                 :        128 :   std::string schema_ser_type;
     844         [ +  + ]:        128 :   const auto schema_ser_source = ser_type.empty() ? std::string_view{next_ser_type} : std::string_view{ser_type};
     845                 :        128 :   SchemaType schema_storage_type = SchemaData::resolve_type(schema_type, schema_ser_source);
     846                 :        128 :   bool has_split_method_schema = false;
     847                 :            : 
     848         [ +  - ]:        128 :   schema_ser_type.assign(schema_ser_source.begin(), schema_ser_source.end());
     849                 :            : 
     850   [ +  +  +  + ]:        118 :   if ((action_type == ActionType::kClientRequest || action_type == ActionType::kClientResponse ||
     851   [ +  +  +  +  :        257 :        action_type == ActionType::kServerRequest || action_type == ActionType::kServerResponse) &&
                   +  + ]
     852         [ +  - ]:         14 :       !schema_ser_source.empty()) {
     853                 :         14 :     const auto split_pos = schema_ser_source.find('|');
     854                 :            : 
     855         [ +  - ]:         14 :     if (split_pos != std::string_view::npos) {
     856         [ +  - ]:         14 :       auto payload_ser_type = schema_ser_source.substr(0, split_pos);
     857                 :            : 
     858   [ +  +  +  + ]:         14 :       if (action_type == ActionType::kClientResponse || action_type == ActionType::kServerResponse) {
     859         [ +  - ]:          3 :         payload_ser_type = schema_ser_source.substr(split_pos + 1);
     860                 :            :       }
     861                 :            : 
     862         [ +  - ]:         14 :       if (!payload_ser_type.empty()) {
     863         [ +  - ]:         14 :         schema_ser_type.assign(payload_ser_type.begin(), payload_ser_type.end());
     864                 :         14 :         schema_storage_type = SchemaData::resolve_type(schema_type, payload_ser_type);
     865                 :         14 :         has_split_method_schema = true;
     866                 :            :       }
     867                 :            :     }
     868                 :            :   }
     869                 :            : 
     870         [ +  + ]:        128 :   if (!next_ser_type.empty()) {
     871   [ +  -  +  + ]:        127 :     if VUNLIKELY (!load_schema(schema_ser_type, schema_storage_type, schema_data)) {
     872                 :          2 :       return false;
     873                 :            :     }
     874                 :            : 
     875                 :        125 :     schema_storage_type = SchemaData::resolve_type(schema_storage_type, schema_ser_type, schema_data.encoding);
     876                 :            : 
     877         [ +  + ]:        125 :     if (has_split_method_schema) {
     878         [ +  + ]:         14 :       if (schema_storage_type != SchemaType::kUnknown) {
     879         [ -  + ]:          6 :         if (next_schema_type == SchemaType::kUnknown) {
     880                 :            :           next_schema_type = schema_storage_type;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     881         [ -  + ]:          6 :         } else if (next_schema_type != schema_storage_type) {
     882                 :            :           next_schema_type = SchemaType::kUnknown;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     883                 :            :         }
     884                 :            :       }
     885                 :            :     } else {
     886         [ +  + ]:        111 :       if (resolved_schema_type == SchemaType::kUnknown) {
     887                 :            :         const auto inferred_schema_type =
     888                 :          1 :             SchemaData::resolve_type(schema_data.schema_type, schema_data.name, schema_data.encoding);
     889                 :            : 
     890         [ +  - ]:          1 :         if (inferred_schema_type != SchemaType::kUnknown) {
     891                 :          1 :           resolved_schema_type = inferred_schema_type;
     892                 :            :         }
     893                 :            :       }
     894                 :            : 
     895         [ +  - ]:        111 :       if (resolved_schema_type != SchemaType::kUnknown) {
     896         [ +  + ]:        111 :         if (next_schema_type == SchemaType::kUnknown) {
     897                 :          1 :           next_schema_type = resolved_schema_type;
     898         [ -  + ]:        110 :         } else if VUNLIKELY (next_schema_type != resolved_schema_type) {
     899                 :            :           CLOG_E("VCAPWriter: URL [%s] schema changed from [%d] to [%d].",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     900                 :            :                  url.c_str(),                                               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     901                 :            :                  static_cast<int>(next_schema_type), static_cast<int>(resolved_schema_type));
     902                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     903                 :            :         }
     904                 :            :       }
     905                 :            :     }
     906                 :            :   } else {
     907                 :          1 :     schema_storage_type = SchemaData::resolve_type(schema_storage_type, schema_ser_type, schema_data.encoding);
     908                 :            :   }
     909                 :            : 
     910         [ +  - ]:        126 :   std::string storage_schema_key = schema_ser_type;
     911         [ +  - ]:        126 :   storage_schema_key.push_back('\x1F');
     912         [ +  - ]:        126 :   storage_schema_key.append(SchemaData::convert_type(schema_storage_type));
     913                 :            : 
     914         [ +  + ]:        126 :   if (total_url_iter_ret.second) {
     915                 :         87 :     total_url_msg_info.index = impl_->total_url_map.size() - 1;
     916         [ +  - ]:         87 :     total_url_msg_info.url = url;
     917         [ +  - ]:         87 :     impl_->total_url_list.emplace_back(channel_key);
     918                 :            :   }
     919                 :            : 
     920   [ +  +  +  + ]:        251 :   if (!schema_ser_type.empty() &&
     921   [ +  +  -  + ]:        125 :       (schema_storage_type == SchemaType::kProtobuf || schema_storage_type == SchemaType::kFlatbuffers)) {
     922         [ +  - ]:         22 :     std::string schema_record_key = schema_ser_type;
     923         [ +  - ]:         22 :     schema_record_key.push_back('\x1F');
     924         [ +  - ]:         22 :     schema_record_key.append(SchemaData::convert_type(schema_storage_type));
     925                 :            : 
     926   [ +  -  +  + ]:         22 :     if (impl_->ser_map.find(schema_record_key) == impl_->ser_map.end()) {
     927   [ +  -  +  -  :         20 :       if (!schema_data.name.empty() && !schema_data.encoding.empty() && !schema_data.data.empty()) {
             +  -  +  - ]
     928                 :         20 :         mcap::Schema schema;
     929                 :         20 :         schema.id = static_cast<mcap::SchemaId>(impl_->ser_map.size() + 1);
     930         [ +  - ]:         20 :         schema.name = schema_data.name;
     931         [ +  - ]:         20 :         schema.encoding = schema_data.encoding;
     932         [ +  - ]:         20 :         schema.data.assign(reinterpret_cast<const std::byte*>(schema_data.data.data()),
     933                 :         20 :                            reinterpret_cast<const std::byte*>(schema_data.data.data()) + schema_data.data.size());
     934                 :            : 
     935         [ +  - ]:         20 :         impl_->writer->addSchema(schema);
     936                 :            : 
     937         [ +  - ]:         20 :         impl_->ser_map.emplace(schema_record_key, schema.id);
     938                 :         20 :       }
     939                 :            :     }
     940                 :         22 :   }
     941                 :            : 
     942         [ +  + ]:        126 :   if (url_iter_ret.second) {
     943                 :        111 :     url_msg_info.index = impl_->url_map.size() - 1;
     944                 :            : 
     945   [ +  +  +  +  :        111 :     if (action_type == ActionType::kClientRequest || action_type == ActionType::kClientResponse ||
                   +  + ]
     946         [ +  + ]:         98 :         action_type == ActionType::kServerRequest || action_type == ActionType::kServerResponse) {
     947         [ +  - ]:         14 :       impl_->write_url_type = "Method";
     948   [ +  +  +  + ]:         97 :     } else if (action_type == ActionType::kPublish || action_type == ActionType::kSubscribe) {
     949         [ +  - ]:         84 :       impl_->write_url_type = "Event";
     950   [ +  +  +  + ]:         13 :     } else if (action_type == ActionType::kSet || action_type == ActionType::kGet) {
     951         [ +  - ]:         12 :       impl_->write_url_type = "Field";
     952                 :            :     } else {
     953         [ +  - ]:          1 :       impl_->write_url_type = "Unknown";
     954                 :            :     }
     955                 :            : 
     956                 :        111 :     mcap::SchemaId schema_id = 0;
     957                 :            : 
     958   [ +  +  +  +  :        111 :     if (impl_->write_url_type != "Method" && !next_ser_type.empty()) {
                   +  + ]
     959         [ +  - ]:         96 :       auto iter = impl_->ser_map.find(storage_schema_key);
     960                 :            : 
     961         [ +  + ]:         96 :       if (iter != impl_->ser_map.end()) {
     962                 :         16 :         schema_id = iter->second;
     963                 :            :       }
     964                 :            :     }
     965                 :            : 
     966                 :        111 :     mcap::Channel channel;
     967                 :        111 :     channel.id = url_msg_info.index + 1;
     968                 :        111 :     channel.schemaId = schema_id;
     969         [ +  - ]:        111 :     channel.topic = url;
     970   [ +  -  +  -  :        111 :     channel.metadata["action"] = std::string(convert_action(action_type));
             +  -  +  - ]
     971                 :            : 
     972         [ +  + ]:        111 :     if (impl_->write_url_type != "Method") {
     973         [ +  + ]:         97 :       if (!schema_data.encoding.empty()) {
     974         [ +  - ]:         96 :         channel.messageEncoding = schema_data.encoding;
     975         [ -  + ]:          1 :       } else if (next_schema_type != SchemaType::kUnknown) {
     976                 :            :         channel.messageEncoding = SchemaData::convert_type(next_schema_type);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     977                 :            :       }
     978                 :            :     }
     979                 :            : 
     980                 :        111 :     url_msg_info.index = channel.id - 1;
     981                 :            : 
     982         [ +  - ]:        111 :     impl_->writer->addChannel(channel);
     983                 :            : 
     984         [ +  - ]:        111 :     url_msg_info.url = url;
     985         [ +  - ]:        111 :     url_msg_info.url_type = impl_->write_url_type;
     986         [ +  - ]:        111 :     total_url_msg_info.url_type = impl_->write_url_type;
     987                 :            : 
     988         [ +  + ]:        111 :     if VLIKELY (!next_ser_type.empty()) {
     989         [ +  - ]:        110 :       url_msg_info.ser_type = next_ser_type;
     990         [ +  - ]:        110 :       total_url_msg_info.ser_type = next_ser_type;
     991                 :            :     }
     992                 :            : 
     993                 :        111 :     url_msg_info.schema_type = next_schema_type;
     994                 :        111 :     total_url_msg_info.schema_type = next_schema_type;
     995                 :        111 :     url_msg_info.action_type = action_type;
     996                 :        111 :     total_url_msg_info.action_type = action_type;
     997                 :        111 :   } else {
     998         [ +  - ]:         15 :     total_url_msg_info.ser_type = next_ser_type;
     999         [ +  - ]:         15 :     url_msg_info.ser_type = next_ser_type;
    1000                 :         15 :     total_url_msg_info.schema_type = next_schema_type;
    1001                 :         15 :     url_msg_info.schema_type = next_schema_type;
    1002                 :            :   }
    1003                 :            : 
    1004                 :            :   // update count
    1005                 :        126 :   ++url_msg_info.count;
    1006                 :        126 :   ++total_url_msg_info.count;
    1007                 :            : 
    1008                 :            :   // update size
    1009                 :        126 :   url_msg_info.size += data.size();
    1010                 :        126 :   total_url_msg_info.size += data.size();
    1011                 :            : 
    1012   [ +  +  +  +  :        126 :   if (action_type == ActionType::kPublish || action_type == ActionType::kSubscribe || action_type == ActionType::kSet ||
             +  +  +  + ]
    1013                 :            :       action_type == ActionType::kGet) {
    1014                 :        111 :     double time_duration = 0;
    1015                 :            : 
    1016         [ +  + ]:        111 :     if (total_url_msg_info.first_timestamp < 0) {
    1017                 :         72 :       total_url_msg_info.first_timestamp = microseconds_timestamp;
    1018                 :            :     }
    1019                 :            : 
    1020                 :        111 :     total_url_msg_info.last_timestamp = microseconds_timestamp;
    1021                 :        111 :     time_duration = (total_url_msg_info.last_timestamp - total_url_msg_info.first_timestamp) / 1000'000.0;
    1022                 :            : 
    1023         [ +  + ]:        111 :     if (time_duration > 0) {
    1024                 :         38 :       total_url_msg_info.freq = total_url_msg_info.count / time_duration;
    1025                 :            :     } else {
    1026                 :         73 :       total_url_msg_info.freq = 0;
    1027                 :            :     }
    1028                 :            : 
    1029         [ +  + ]:        111 :     if (url_msg_info.first_timestamp < 0) {
    1030                 :         96 :       url_msg_info.first_timestamp = microseconds_timestamp;
    1031                 :            :     }
    1032                 :            : 
    1033                 :        111 :     url_msg_info.last_timestamp = microseconds_timestamp;
    1034                 :        111 :     time_duration = (url_msg_info.last_timestamp - url_msg_info.first_timestamp) / 1000'000.0;
    1035                 :            : 
    1036         [ +  + ]:        111 :     if (time_duration > 0) {
    1037                 :         14 :       url_msg_info.freq = url_msg_info.count / time_duration;
    1038                 :            :     } else {
    1039                 :         97 :       url_msg_info.freq = 0;
    1040                 :            :     }
    1041                 :            :   }
    1042                 :            : 
    1043                 :            :   // insert data
    1044                 :            : 
    1045         [ +  + ]:        126 :   if VUNLIKELY (impl_->last_timestamp > microseconds_timestamp) {
    1046         [ +  - ]:          2 :     if (impl_->last_timestamp - microseconds_timestamp < 1000'00U) {
    1047                 :          2 :       microseconds_timestamp = impl_->last_timestamp + 1;
    1048                 :            :     }
    1049                 :            :   }
    1050                 :            : 
    1051                 :        126 :   impl_->last_timestamp = microseconds_timestamp;
    1052                 :            : 
    1053                 :        126 :   mcap::Message message;
    1054                 :        126 :   message.channelId = url_msg_info.index + 1;
    1055                 :        126 :   message.sequence = url_msg_info.count;
    1056                 :        126 :   message.logTime = microseconds_timestamp * 1000U + impl_->start_timestamp * 1000'000;
    1057                 :        126 :   message.publishTime = message.logTime;
    1058                 :        126 :   message.data = reinterpret_cast<const std::byte*>(data.data());
    1059                 :        126 :   message.dataSize = data.size();
    1060                 :            : 
    1061         [ +  - ]:        126 :   status = impl_->writer->write(message);
    1062                 :            : 
    1063         [ -  + ]:        126 :   if VUNLIKELY (!status.ok()) {
    1064                 :            :     CLOG_W("VCAPWriter: Failed to write message data, error = %s.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1065                 :            :            status.message.c_str());                                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1066                 :            :     return false;                                                    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1067                 :            :   }
    1068                 :            : 
    1069                 :        126 :   impl_->cached_size.fetch_add(data.size(), std::memory_order_relaxed);
    1070                 :            : 
    1071                 :        126 :   ++impl_->current_row;
    1072                 :        126 :   impl_->current_size += data.size();
    1073                 :            : 
    1074                 :        126 :   ++impl_->total_current_row;
    1075                 :        126 :   impl_->total_current_size += data.size();
    1076                 :        126 :   impl_->total_timestamp = microseconds_timestamp;
    1077                 :            : 
    1078                 :        126 :   return true;
    1079                 :        130 : }
    1080                 :            : 
    1081                 :         73 : bool VCAPWriter::write_filex(bool complete) {
    1082                 :            :   try {
    1083                 :            : #ifdef _WIN32
    1084                 :            :     std::filesystem::path file_path(Helpers::string_to_wstring(impl_->path));
    1085                 :            : #else
    1086         [ +  - ]:         73 :     std::filesystem::path file_path(impl_->path);
    1087                 :            : #endif
    1088                 :            : 
    1089                 :         73 :     nlohmann::ordered_json json;
    1090                 :            : 
    1091         [ +  - ]:         73 :     json["VLinkHeader"] = {
    1092                 :            :         {"major", VLINK_VERSION_MAJOR},  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1093                 :            :         {"minor", VLINK_VERSION_MINOR},  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1094                 :            :         {"patch", VLINK_VERSION_PATCH},  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1095                 :         73 :         {"count", impl_->total_current_row},
    1096                 :         73 :         {"duration", impl_->total_timestamp},
    1097                 :            :         {"accuracy", "MicroSecond"},
    1098                 :         73 :         {"compress", impl_->enable_compressed ? "zstd" : "None"},
    1099                 :         73 :         {"process", impl_->app_name},
    1100         [ +  - ]:         73 :         {"date", get_format_date(&impl_->time_start)},
    1101                 :         73 :         {"tag", impl_->tag_name},
    1102         [ +  + ]:         73 :         {"split_by_size", impl_->config.split_by_time > 0 ? 0 : impl_->config.split_by_size},
    1103                 :         73 :         {"split_by_time", impl_->config.split_by_time},
    1104                 :            :         {"complete", complete},
    1105                 :         73 :         {"timezone", impl_->timezone_diff},
    1106                 :         73 :         {"start_timestamp", impl_->start_timestamp},
    1107   [ +  -  +  -  :       4161 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
             -  -  -  - ]
    1108                 :            : 
    1109                 :         73 :     nlohmann::ordered_json url_json;
    1110                 :            : 
    1111                 :            :     {
    1112   [ +  -  +  - ]:         73 :       std::lock_guard lock(sample_mutex());
    1113                 :            : 
    1114         [ +  + ]:        123 :       for (const auto& url : impl_->total_url_list) {
    1115         [ +  - ]:         50 :         const auto& ext_info = impl_->total_url_map[url];
    1116   [ +  -  +  -  :         50 :         auto loss = total_url_loss_map_ref()[recover_recorded_url(ext_info.url)];
                   +  - ]
    1117                 :            : 
    1118   [ +  -  +  -  :       2100 :         url_json.push_back({
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
    1119                 :            :             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1120                 :         50 :             {"index", ext_info.index},
    1121         [ +  - ]:         50 :             {"url", ext_info.url},
    1122         [ +  - ]:         50 :             {"type", ext_info.url_type},
    1123   [ +  -  +  - ]:        100 :             {"action", std::string(convert_action(ext_info.action_type))},
    1124         [ +  - ]:         50 :             {"ser", ext_info.ser_type},
    1125         [ +  - ]:        100 :             {"encoding", std::string(SchemaData::convert_type(ext_info.schema_type))},
    1126                 :         50 :             {"count", ext_info.count},
    1127                 :         50 :             {"size", ext_info.size},
    1128                 :            :             {"loss", loss},
    1129                 :         50 :             {"freq", ext_info.freq},
    1130                 :            :         });
    1131                 :            :       }
    1132                 :         73 :     }
    1133                 :            : 
    1134         [ +  - ]:         73 :     json["VLinkUrls"] = std::move(url_json);
    1135                 :            : 
    1136                 :         73 :     nlohmann::ordered_json files_json;
    1137         [ +  + ]:        149 :     for (const auto& file : impl_->split_file_list) {
    1138   [ +  -  +  - ]:         76 :       files_json.push_back(file);
    1139                 :            :     }
    1140                 :            : 
    1141         [ +  - ]:         73 :     json["VLinkFiles"] = std::move(files_json);
    1142                 :            : 
    1143         [ +  - ]:         73 :     std::ofstream filex(file_path);
    1144                 :            : 
    1145   [ +  -  +  - ]:         73 :     if VLIKELY (filex.is_open()) {
    1146   [ +  -  +  - ]:         73 :       filex << json.dump(4);
    1147         [ +  - ]:         73 :       filex.close();
    1148                 :            :     }
    1149         [ -  - ]:         73 :   } catch (nlohmann::json::exception& e) {
    1150                 :            :     VLOG_F("VCAPWriter: Filesystem error, ", e.what(), ".");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1151                 :            :     return false;                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1152                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1153                 :            : 
    1154                 :         73 :   return true;
    1155                 :            : }
    1156                 :            : 
    1157                 :            : }  // namespace vlink

Generated by: LCOV version 1.14