LCOV - code coverage report
Current view: top level - src/extension - vcap_reader.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 895 898 99.7 %
Date: 2026-06-27 19:56:04 Functions: 81 84 96.4 %
Branches: 824 1348 61.1 %

           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_reader.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <atomic>
      28                 :            : #include <cinttypes>
      29                 :            : #include <filesystem>
      30                 :            : #include <fstream>
      31                 :            : #include <limits>
      32                 :            : #include <memory>
      33                 :            : #include <mutex>
      34                 :            : #include <shared_mutex>
      35                 :            : #include <string>
      36                 :            : #include <string_view>
      37                 :            : #include <unordered_map>
      38                 :            : #include <utility>
      39                 :            : #include <vector>
      40                 :            : 
      41                 :            : #include "./base/condition_variable.h"
      42                 :            : #include "./base/elapsed_timer.h"
      43                 :            : #include "./base/helpers.h"
      44                 :            : #include "./base/logger.h"
      45                 :            : #include "./version.h"
      46                 :            : 
      47                 :            : // json
      48                 :            : #include <nlohmann/json.hpp>
      49                 :            : 
      50                 :            : // mcap
      51                 :            : #include "./private/mcap_import.h"
      52                 :            : 
      53                 :            : namespace vlink {
      54                 :            : 
      55                 :            : [[maybe_unused]] static constexpr size_t kMaxTaskSize = 50000U;
      56                 :            : 
      57                 :            : // VCAPReader::Impl
      58                 :            : struct VCAPReader::Impl final {  // NOLINT(clang-analyzer-optin.performance.Padding)
      59                 :            :   std::atomic<BagReader::Status> status{VCAPReader::kStopped};
      60                 :            :   std::atomic_bool stop_flag{false};
      61                 :            :   std::atomic_bool pause_flag{false};
      62                 :            :   std::atomic_bool pause_next_flag{false};
      63                 :            :   std::atomic_bool jump_flag{false};
      64                 :            :   std::atomic<int64_t> pause_elapsed{0};
      65                 :            :   std::atomic<int64_t> offset_elapsed{0};
      66                 :            :   std::atomic<int64_t> real_elapsed{0};
      67                 :            :   std::atomic<int64_t> extra_elapsed{0};
      68                 :            :   std::atomic<int64_t> begin_time{0};
      69                 :            :   std::atomic<double> rate{1.0};
      70                 :            :   std::atomic<int> times{1};
      71                 :            :   std::atomic_bool is_pending{false};
      72                 :            :   std::atomic<int> split_index{0};
      73                 :            : 
      74                 :            :   bool read_only{false};
      75                 :            :   bool try_to_fix{false};
      76                 :            :   bool enable_compress{false};
      77                 :            : 
      78                 :            :   std::string path;
      79                 :            :   BagReader::Info info;
      80                 :            :   std::vector<BagReader::Info::UrlMeta> raw_url_metas;
      81                 :            :   std::mutex mtx;
      82                 :            :   ConditionVariable cv;
      83                 :            : 
      84                 :            :   BagReader::Config config;
      85                 :            :   std::mutex config_mtx;
      86                 :            :   std::shared_mutex time_mtx;
      87                 :            : 
      88                 :            :   ElapsedTimer elapsed_timer{ElapsedTimer::kMicro};
      89                 :            :   ElapsedTimer pause_elapsed_timer{ElapsedTimer::kMicro};
      90                 :            :   ElapsedTimer offset_timer{ElapsedTimer::kMicro};
      91                 :            :   ElapsedTimer real_timer{ElapsedTimer::kMicro};
      92                 :            : 
      93                 :            :   BagReader::StatusCallback status_callback;
      94                 :            :   BagReader::ReadyCallback ready_callback;
      95                 :            :   BagReader::FinishCallback finish_callback;
      96                 :            : 
      97                 :            :   int64_t total_start_timestamp_ns{-1};
      98                 :            :   bool total_has_completed{false};
      99                 :            : 
     100                 :            :   // mcap
     101                 :            : 
     102                 :            :   // WrapperFile
     103                 :            :   struct WrapperFile final {
     104                 :            :     std::string path;
     105                 :            :     std::unique_ptr<mcap::McapReader> reader;
     106                 :            :     std::unique_ptr<mcap::LinearMessageView> msg_view;
     107                 :            :     std::optional<mcap::LinearMessageView::Iterator> msg_view_begin;
     108                 :            :     std::optional<mcap::LinearMessageView::Iterator> msg_view_end;
     109                 :            :     int index{0};
     110                 :            :     int64_t start_timestamp_ns{0};
     111                 :            :     int64_t begin{0};
     112                 :            :     int64_t end{0};
     113                 :            :     std::unordered_map<std::string, int> url_to_id_map;
     114                 :            :     std::unordered_map<int, std::string> id_to_url_map;
     115                 :            :     std::unordered_map<int, ActionType> channel_action_map;
     116                 :            :     bool has_idx_elapsed{false};
     117                 :            :     bool has_idx_url{false};
     118                 :            :     bool has_schema{false};
     119                 :            :     bool has_completed{false};
     120                 :            :     bool is_channel_broken{false};
     121                 :            : 
     122                 :         88 :     WrapperFile() {
     123         [ +  - ]:         88 :       url_to_id_map.reserve(128);
     124         [ +  - ]:         88 :       id_to_url_map.reserve(128);
     125         [ +  - ]:         88 :       channel_action_map.reserve(128);
     126                 :         88 :     }
     127                 :            :   };
     128                 :            : 
     129                 :            :   std::vector<WrapperFile> file_list;
     130                 :            : 
     131                 :            :   std::unique_ptr<mcap::LinearMessageView> cursor_msg_view;
     132                 :            :   std::optional<mcap::LinearMessageView::Iterator> cursor_iter;
     133                 :            :   std::optional<mcap::LinearMessageView::Iterator> cursor_iter_end;
     134                 :            :   int cursor_file_index{0};
     135                 :            :   int64_t cursor_begin_us{0};
     136                 :            :   int64_t cursor_end_us{0};
     137                 :            :   bool cursor_need_advance{false};
     138                 :            :   bool cursor_read_error{false};
     139                 :            :   BagReader::Config cursor_config;
     140                 :            : };
     141                 :            : 
     142                 :            : // VCAPReader
     143                 :         67 : VCAPReader::VCAPReader(const std::string& path, bool read_only, bool try_to_fix)
     144         [ +  - ]:         67 :     : BagReader(path, read_only, try_to_fix), impl_{std::make_unique<Impl>()} {
     145   [ +  -  +  - ]:         67 :   set_name("VCAPReader");
     146                 :            : 
     147   [ +  -  +  - ]:         67 :   url_ser_map().reserve(128);
     148   [ +  -  +  - ]:         67 :   url_schema_type_map().reserve(128);
     149                 :            : 
     150                 :         67 :   impl_->read_only = read_only;
     151                 :         67 :   impl_->try_to_fix = try_to_fix;
     152                 :            : 
     153         [ +  + ]:         67 :   open(path);
     154                 :         74 : }
     155                 :            : 
     156                 :         60 : VCAPReader::~VCAPReader() {
     157         [ +  + ]:         60 :   if (!impl_->stop_flag.load(std::memory_order_relaxed)) {
     158                 :         59 :     do_stop();
     159                 :            :   }
     160                 :            : 
     161                 :         60 :   quit(true);
     162                 :            : 
     163                 :         60 :   impl_->cv.notify_one();
     164                 :            : 
     165                 :         60 :   wait_for_quit();
     166                 :            : 
     167                 :         60 :   detach_plugin();
     168                 :            : 
     169                 :         60 :   close();
     170                 :         60 : }
     171                 :            : 
     172                 :          3 : void VCAPReader::bind_plugin_interface(const std::shared_ptr<BagPluginInterface>& plugin_interface) {
     173                 :          3 :   BagReader::bind_plugin_interface(plugin_interface);
     174                 :          3 :   impl_->info.url_metas = impl_->raw_url_metas;
     175                 :          3 :   process_url_metas(impl_->info.url_metas);
     176                 :          3 :   rebuild_url_meta_lookup(impl_->info.url_metas);
     177                 :          3 : }
     178                 :            : 
     179                 :          2 : void VCAPReader::register_status_callback(StatusCallback&& status_callback) {
     180                 :          2 :   impl_->status_callback = std::move(status_callback);
     181                 :          2 : }
     182                 :            : 
     183                 :          2 : void VCAPReader::register_ready_callback(ReadyCallback&& ready_callback) {
     184                 :          2 :   impl_->ready_callback = std::move(ready_callback);
     185                 :          2 : }
     186                 :            : 
     187                 :          4 : void VCAPReader::register_finish_callback(FinishCallback&& finish_callback) {
     188                 :          4 :   impl_->finish_callback = std::move(finish_callback);
     189                 :          4 : }
     190                 :            : 
     191                 :          5 : void VCAPReader::register_output_callback(OutputCallback&& output_callback) {
     192                 :          5 :   BagReader::register_output_callback(std::move(output_callback));
     193                 :          5 : }
     194                 :            : 
     195                 :          9 : void VCAPReader::play(const Config& config) {
     196   [ +  -  -  + ]:          9 :   if VUNLIKELY (is_busy()) {
     197   [ #  #  #  # ]:          0 :     VLOG_W("VCAPReader: Is busy.");
     198                 :            :     // return;
     199                 :            :   }
     200                 :            : 
     201         [ +  + ]:          9 :   if (config.skip_blank) {
     202                 :          1 :     impl_->begin_time.store(std::max(config.begin_time, impl_->info.blank_duration), std::memory_order_relaxed);
     203                 :            :   } else {
     204                 :          8 :     impl_->begin_time.store(config.begin_time, std::memory_order_relaxed);
     205                 :            :   }
     206                 :            : 
     207         [ +  + ]:          9 :   if (config.rate <= 0) {
     208                 :          1 :     impl_->rate.store(1, std::memory_order_relaxed);
     209                 :            :   } else {
     210                 :          8 :     impl_->rate.store(config.rate, std::memory_order_relaxed);
     211                 :            :   }
     212                 :            : 
     213                 :          9 :   impl_->times.store(config.times, std::memory_order_relaxed);
     214                 :            : 
     215                 :         18 :   impl_->real_elapsed.store(impl_->begin_time.load(std::memory_order_relaxed) * 1000U, std::memory_order_relaxed);
     216                 :          9 :   impl_->is_pending.store(true, std::memory_order_relaxed);
     217                 :            : 
     218                 :            :   {
     219         [ +  - ]:          9 :     std::unique_lock lock(impl_->mtx);
     220                 :          9 :     impl_->stop_flag.store(false, std::memory_order_relaxed);
     221                 :          9 :     impl_->pause_flag.store(false, std::memory_order_relaxed);
     222                 :          9 :     impl_->pause_next_flag.store(false, std::memory_order_relaxed);
     223                 :          9 :     impl_->jump_flag.store(false, std::memory_order_relaxed);
     224                 :          9 :   }
     225                 :            : 
     226                 :          9 :   Config config_snapshot;
     227                 :            : 
     228                 :            :   {
     229         [ +  - ]:          9 :     std::unique_lock lock(impl_->config_mtx);
     230         [ +  - ]:          9 :     impl_->config = config;
     231         [ +  - ]:          9 :     config_snapshot = impl_->config;
     232                 :          9 :   }
     233                 :            : 
     234   [ +  -  +  -  :         18 :   post_task([this, config_snapshot]() { read(config_snapshot); });
                   +  - ]
     235                 :          9 : }
     236                 :            : 
     237                 :          2 : void VCAPReader::stop() { do_stop(); }
     238                 :            : 
     239                 :          2 : void VCAPReader::pause() {
     240                 :            :   {
     241         [ +  - ]:          2 :     std::unique_lock lock(impl_->mtx);
     242                 :          2 :     impl_->pause_flag.store(true, std::memory_order_relaxed);
     243                 :          2 :   }
     244                 :            : 
     245                 :          2 :   impl_->cv.notify_one();
     246                 :          2 : }
     247                 :            : 
     248                 :          2 : void VCAPReader::resume() {
     249                 :            :   {
     250         [ +  - ]:          2 :     std::unique_lock lock(impl_->mtx);
     251                 :          2 :     impl_->pause_flag.store(false, std::memory_order_relaxed);
     252                 :          2 :   }
     253                 :            : 
     254                 :          2 :   impl_->cv.notify_one();
     255                 :          2 : }
     256                 :            : 
     257                 :          4 : void VCAPReader::pause_to_next() {
     258                 :            :   {
     259         [ +  - ]:          4 :     std::unique_lock lock(impl_->mtx);
     260                 :            : 
     261         [ +  + ]:          4 :     if (!impl_->pause_flag.load(std::memory_order_relaxed)) {
     262                 :          1 :       return;
     263                 :            :     }
     264                 :            : 
     265                 :          3 :     impl_->pause_next_flag.store(true, std::memory_order_relaxed);
     266         [ +  + ]:          4 :   }
     267                 :            : 
     268                 :          3 :   impl_->cv.notify_one();
     269                 :            : }
     270                 :            : 
     271                 :          4 : void VCAPReader::jump(int64_t begin_time, double rate, int times, bool force_to_play) {
     272         [ +  + ]:          4 :   if (begin_time < 0) {
     273                 :          1 :     begin_time = 0;
     274         [ +  + ]:          3 :   } else if (begin_time > impl_->info.total_duration) {
     275                 :          1 :     begin_time = std::max<int64_t>(0, impl_->info.total_duration - 100);
     276                 :            :   }
     277                 :            : 
     278                 :          4 :   impl_->real_elapsed.store(begin_time * 1000U, std::memory_order_relaxed);
     279                 :          4 :   impl_->is_pending.store(true, std::memory_order_relaxed);
     280                 :            : 
     281                 :          4 :   bool last_pause_flag = impl_->pause_flag.load(std::memory_order_relaxed);
     282                 :            : 
     283                 :            :   {
     284         [ +  - ]:          4 :     std::unique_lock lock(impl_->mtx);
     285                 :          4 :     impl_->stop_flag.store(false, std::memory_order_relaxed);
     286                 :          4 :     impl_->pause_flag.store(false, std::memory_order_relaxed);
     287                 :          4 :     impl_->pause_next_flag.store(false, std::memory_order_relaxed);
     288                 :          4 :     impl_->jump_flag.store(true, std::memory_order_relaxed);
     289                 :          4 :   }
     290                 :            : 
     291                 :          4 :   impl_->cv.notify_one();
     292                 :            : 
     293         [ +  - ]:          4 :   wait_for_idle();
     294                 :            : 
     295                 :          4 :   impl_->begin_time.store(begin_time, std::memory_order_relaxed);
     296                 :            : 
     297         [ +  + ]:          4 :   if (rate <= 0) {
     298                 :          3 :     impl_->rate.store(1, std::memory_order_relaxed);
     299                 :            :   } else {
     300                 :          1 :     impl_->rate.store(rate, std::memory_order_relaxed);
     301                 :            :   }
     302                 :            : 
     303                 :          4 :   impl_->times.store(times, std::memory_order_relaxed);
     304                 :            : 
     305                 :            :   {
     306         [ +  - ]:          4 :     std::unique_lock lock(impl_->mtx);
     307                 :          4 :     impl_->stop_flag.store(false, std::memory_order_relaxed);
     308         [ +  + ]:          5 :     impl_->pause_flag.store(force_to_play ? false : last_pause_flag, std::memory_order_relaxed);
     309                 :          4 :     impl_->pause_next_flag.store(false, std::memory_order_relaxed);
     310                 :          4 :     impl_->jump_flag.store(false, std::memory_order_relaxed);
     311                 :          4 :   }
     312                 :            : 
     313                 :          4 :   Config config_snapshot;
     314                 :            : 
     315                 :            :   {
     316         [ +  - ]:          4 :     std::unique_lock lock(impl_->config_mtx);
     317         [ +  - ]:          4 :     config_snapshot = impl_->config;
     318                 :          4 :   }
     319                 :            : 
     320   [ +  -  +  -  :          8 :   post_task([this, config_snapshot]() { read(config_snapshot); });
                   +  - ]
     321                 :          5 : }
     322                 :            : 
     323                 :         32 : std::future<bool> VCAPReader::check() {
     324         [ +  + ]:         32 :   if VUNLIKELY (is_busy()) {
     325   [ +  -  +  - ]:          2 :     VLOG_W("VCAPReader: Is busy.");
     326                 :            :     // return std::future<bool>();
     327                 :            :   }
     328                 :            : 
     329                 :        378 :   return invoke_task([this]() {
     330         [ +  + ]:         32 :     if (!impl_->total_has_completed) {
     331   [ +  -  +  - ]:          2 :       VLOG_W("VCAPReader: Incomplete data detected.");
     332                 :          1 :       return false;
     333                 :            :     }
     334                 :            : 
     335                 :         31 :     mcap::Status status;
     336                 :            : 
     337                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     338                 :            :     auto status_function = [](const mcap::Status& status) {
     339                 :            :       if (!status.ok()) {
     340                 :            :         CLOG_W("VCAPReader: Failed to check summary, error = %s.", status.message.c_str());
     341                 :            :       }
     342                 :            :     };
     343                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     344                 :            : 
     345         [ +  + ]:         81 :     for (auto& wrapper_file : impl_->file_list) {
     346         [ -  + ]:         50 :       if VUNLIKELY (!wrapper_file.reader) {
     347                 :            :         continue;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     348                 :            :       }
     349                 :            : 
     350         [ +  - ]:         50 :       status = wrapper_file.reader->readSummary(mcap::ReadSummaryMethod::ForceScan, status_function);
     351                 :            : 
     352         [ -  + ]:         50 :       if VUNLIKELY (!status.ok()) {
     353                 :            :         CLOG_W("VCAPReader: Failed to check whole summary, error = %s.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     354                 :            :                status.message.c_str());                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     355                 :            :         return false;                                                     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     356                 :            :       }
     357                 :            :     }
     358                 :            : 
     359                 :         31 :     bool is_ok = true;
     360                 :            : 
     361         [ +  + ]:         31 :     if VUNLIKELY (impl_->info.total_duration < impl_->info.blank_duration) {
     362   [ +  -  +  - ]:          2 :       CLOG_W("VCAPReader: Invalid duration, blank=%" PRId64 " total=%" PRId64 ".",
     363                 :            :              static_cast<int64_t>(impl_->info.blank_duration), static_cast<int64_t>(impl_->info.total_duration));
     364                 :          1 :       is_ok = false;
     365                 :            :     }
     366                 :            : 
     367   [ +  +  +  +  :         31 :     if VUNLIKELY (impl_->info.message_count > 0 && impl_->info.url_metas.empty()) {
                   +  + ]
     368   [ +  -  +  - ]:          2 :       CLOG_W("VCAPReader: Message count is %" PRId64 " but url meta list is empty.",
     369                 :            :              static_cast<int64_t>(impl_->info.message_count));
     370                 :          1 :       is_ok = false;
     371                 :            :     }
     372                 :            : 
     373                 :         31 :     size_t total_count = 0;
     374                 :         31 :     size_t total_raw_size = 0;
     375                 :            : 
     376         [ +  + ]:         78 :     for (const auto& url_meta : impl_->info.url_metas) {
     377                 :         47 :       total_count += url_meta.count;
     378                 :         47 :       total_raw_size += url_meta.size;
     379                 :            : 
     380         [ -  + ]:         47 :       if VUNLIKELY (!url_meta.valid) {
     381                 :            :         CLOG_W("VCAPReader: Invalid url meta detected at index=%d.", url_meta.index);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     382                 :            :         is_ok = false;                                                                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     383                 :            :       }
     384                 :            : 
     385         [ +  + ]:         47 :       if VUNLIKELY (url_meta.url.empty()) {
     386   [ +  -  +  - ]:          2 :         CLOG_W("VCAPReader: Empty url detected at index=%d.", url_meta.index);
     387                 :          1 :         is_ok = false;
     388                 :            :       }
     389                 :            : 
     390         [ +  + ]:         47 :       if VUNLIKELY (url_meta.url_type.empty()) {
     391   [ +  -  +  - ]:          2 :         CLOG_W("VCAPReader: Empty url_type detected for url=%s.", url_meta.url.c_str());
     392                 :          1 :         is_ok = false;
     393                 :            :       }
     394                 :            : 
     395   [ +  -  +  +  :         47 :       if VUNLIKELY (url_meta.count > 0 && url_meta.ser_type.empty()) {
                   +  + ]
     396   [ +  -  +  - ]:          4 :         CLOG_W("VCAPReader: Empty ser_type detected for url=%s.", url_meta.url.c_str());
     397                 :          2 :         is_ok = false;
     398                 :            :       }
     399                 :            : 
     400         [ -  + ]:         47 :       if VUNLIKELY (!SchemaData::is_valid_type(url_meta.schema_type)) {
     401                 :            :         CLOG_W("VCAPReader: Invalid schema_type=%d detected for url=%s.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     402                 :            :                static_cast<int>(url_meta.schema_type),                     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     403                 :            :                url_meta.url.c_str());
     404                 :            :         is_ok = false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     405                 :            :       }
     406                 :            : 
     407                 :         47 :       auto inferred_schema_type = SchemaData::infer_ser_type(url_meta.ser_type);
     408                 :            : 
     409   [ +  +  +  +  :         47 :       if VUNLIKELY (url_meta.schema_type == SchemaType::kUnknown && inferred_schema_type != SchemaType::kUnknown) {
                   +  + ]
     410                 :          1 :         const auto schema_label = SchemaData::convert_type(inferred_schema_type);
     411   [ +  -  +  - ]:          2 :         CLOG_W("VCAPReader: Missing schema_type for url=%s, inferred=%.*s.", url_meta.url.c_str(),
     412                 :            :                static_cast<int>(schema_label.size()), schema_label.data());
     413                 :          1 :         is_ok = false;
     414                 :            :       }
     415                 :            : 
     416   [ +  +  +  +  :         47 :       if VUNLIKELY (url_meta.loss < 0.0 || url_meta.loss > 1.0) {
                   +  + ]
     417   [ +  -  +  - ]:          6 :         CLOG_W("VCAPReader: Invalid loss=%f detected for url=%s.", url_meta.loss, url_meta.url.c_str());
     418                 :          3 :         is_ok = false;
     419                 :            :       }
     420                 :            : 
     421         [ +  + ]:         47 :       if VUNLIKELY (url_meta.freq < 0.0) {
     422   [ +  -  +  - ]:          6 :         CLOG_W("VCAPReader: Invalid freq=%f detected for url=%s.", url_meta.freq, url_meta.url.c_str());
     423                 :          3 :         is_ok = false;
     424                 :            :       }
     425                 :            :     }
     426                 :            : 
     427   [ +  +  +  +  :         61 :     if ((!impl_->info.url_metas.empty() || impl_->info.message_count != 0) &&
                   +  + ]
     428         [ +  + ]:         30 :         total_count != static_cast<size_t>(impl_->info.message_count)) {
     429   [ +  -  +  - ]:          4 :       CLOG_W("VCAPReader: Message count mismatch, header=%" PRId64 " metas=%zu.",
     430                 :            :              static_cast<int64_t>(impl_->info.message_count), total_count);
     431                 :          2 :       is_ok = false;
     432                 :            :     }
     433                 :            : 
     434   [ +  +  -  +  :         60 :     if ((!impl_->info.url_metas.empty() || impl_->info.total_raw_size != 0) &&
                   -  + ]
     435         [ -  + ]:         29 :         total_raw_size != static_cast<size_t>(impl_->info.total_raw_size)) {
     436                 :            :       CLOG_W("VCAPReader: Raw size mismatch, header=%" PRId64 " metas=%zu.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     437                 :            :              static_cast<int64_t>(impl_->info.total_raw_size), total_raw_size);
     438                 :            :       is_ok = false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     439                 :            :     }
     440                 :            : 
     441   [ +  -  +  + ]:         38 :     for (const auto& schema_data : detect_schema()) {
     442         [ -  + ]:          7 :       if VUNLIKELY (schema_data.name.empty()) {
     443                 :            :         CLOG_W("VCAPReader: Empty schema name detected.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     444                 :            :         is_ok = false;                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     445                 :            :       }
     446                 :            : 
     447         [ -  + ]:          7 :       if VUNLIKELY (schema_data.encoding.empty()) {
     448                 :            :         CLOG_W("VCAPReader: Empty schema encoding detected for name=%s.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     449                 :            :                schema_data.name.c_str());                                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     450                 :            :         is_ok = false;                                                     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     451                 :            :       }
     452                 :            : 
     453   [ +  -  +  +  :          7 :       if VUNLIKELY (!SchemaData::is_valid_type(schema_data.schema_type) ||
                   +  + ]
     454                 :            :                     schema_data.schema_type == SchemaType::kUnknown) {
     455   [ +  -  +  - ]:          2 :         CLOG_W("VCAPReader: Invalid schema_type=%d detected for schema=%s.", static_cast<int>(schema_data.schema_type),
     456                 :            :                schema_data.name.c_str());
     457                 :          1 :         is_ok = false;
     458                 :            :       }
     459                 :         31 :     }
     460                 :            : 
     461                 :         31 :     return is_ok;
     462         [ +  - ]:         63 :   });
     463                 :            : }
     464                 :            : 
     465                 :          3 : std::future<bool> VCAPReader::reindex() {
     466         [ +  + ]:          3 :   if VUNLIKELY (is_busy()) {
     467   [ +  -  +  - ]:          2 :     VLOG_W("VCAPReader: Is busy.");
     468                 :            :     // return std::future<bool>();
     469                 :            :   }
     470                 :            : 
     471                 :          3 :   return invoke_task([]() {
     472   [ +  -  +  - ]:          6 :     VLOG_W("VCAPReader: Reindex is not supported for vcap.");
     473                 :            : 
     474                 :          3 :     return false;
     475         [ +  - ]:          3 :   });
     476                 :            : }
     477                 :            : 
     478                 :          3 : std::future<bool> VCAPReader::fix(bool rebuild) {
     479         [ +  + ]:          3 :   if VUNLIKELY (is_busy()) {
     480   [ +  -  +  - ]:          2 :     VLOG_W("VCAPReader: Is busy.");
     481                 :            :     // return std::future<bool>();
     482                 :            :   }
     483                 :            : 
     484                 :          3 :   return invoke_task([rebuild]() {
     485                 :            :     (void)rebuild;
     486                 :            : 
     487   [ +  -  +  - ]:          6 :     VLOG_W("VCAPReader: Fix is not supported for vcap.");
     488                 :            : 
     489                 :          3 :     return false;
     490         [ +  - ]:          3 :   });
     491                 :            : }
     492                 :            : 
     493                 :          8 : void VCAPReader::tag(const std::string& tag_name) {
     494         [ +  + ]:          8 :   if VUNLIKELY (is_busy()) {
     495   [ +  -  +  - ]:          2 :     VLOG_W("VCAPReader: Is busy.");
     496                 :            :     // return;
     497                 :            :   }
     498                 :            : 
     499   [ +  -  +  - ]:          8 :   post_task([this, tag_name]() {
     500                 :            :     try {
     501                 :            : #ifdef _WIN32
     502                 :            :       std::filesystem::path file_path(Helpers::string_to_wstring(impl_->path));
     503                 :            :       std::string suffix = Helpers::path_to_string(file_path.extension());
     504                 :            : #else
     505         [ +  - ]:          8 :       std::filesystem::path file_path(impl_->path);
     506   [ +  -  +  - ]:          8 :       std::string suffix = file_path.extension().string();
     507                 :            : #endif
     508                 :            : 
     509                 :         52 :       std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) { return std::tolower(c); });
     510                 :            : 
     511         [ +  + ]:          8 :       if (suffix == ".vcapx") {
     512                 :            :         try {
     513                 :          4 :           nlohmann::ordered_json root_json;
     514                 :          4 :           nlohmann::ordered_json header_json;
     515                 :            : 
     516                 :            :           {
     517         [ +  - ]:          4 :             std::ifstream file(file_path);
     518                 :            : 
     519         [ +  + ]:          4 :             file >> root_json;
     520                 :            : 
     521         [ +  - ]:          3 :             file.close();
     522                 :          4 :           }
     523                 :            : 
     524   [ +  -  +  - ]:          3 :           header_json = root_json["VLinkHeader"];
     525                 :            : 
     526   [ +  -  +  - ]:          3 :           header_json["tag"] = tag_name;
     527                 :            : 
     528         [ +  - ]:          3 :           root_json["VLinkHeader"] = std::move(header_json);
     529                 :            : 
     530                 :            :           {
     531         [ +  - ]:          3 :             std::ofstream filex(impl_->path, std::ios::out | std::ios::trunc);
     532                 :            : 
     533   [ +  -  +  - ]:          3 :             if VLIKELY (filex.is_open()) {
     534   [ +  -  +  - ]:          3 :               filex << root_json.dump(4);
     535         [ +  - ]:          3 :               filex.close();
     536                 :            :             }
     537                 :          3 :           }
     538         [ -  + ]:          6 :         } catch (nlohmann::json::exception& e) {
     539   [ +  -  +  - ]:          2 :           VLOG_W("VCAPReader: JSON parse error, ", e.what(), ".");
     540                 :          1 :         }
     541                 :            :       } else {
     542   [ +  -  +  - ]:          8 :         VLOG_W("VCAPReader: Tag is not supported for single vcap.");
     543                 :            :       }
     544         [ -  - ]:          8 :     } catch (std::filesystem::filesystem_error& e) {
     545                 :            :       VLOG_F("VCAPReader: Filesystem error, ", e.what(), ".");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     546                 :            :       return;                                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     547                 :            :     }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     548                 :            :   });
     549                 :          8 : }
     550                 :            : 
     551                 :          5 : int64_t VCAPReader::get_timestamp() const {
     552         [ +  - ]:          5 :   std::shared_lock time_lock(impl_->time_mtx);
     553                 :            : 
     554         [ +  + ]:          5 :   if (impl_->status.load(std::memory_order_relaxed) == kPlaying) {
     555         [ -  + ]:          1 :     if (impl_->is_pending.load(std::memory_order_relaxed)) {
     556                 :            :       return impl_->real_elapsed.load(std::memory_order_relaxed) / 1000U;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     557                 :            :     } else {
     558                 :          1 :       return (impl_->real_elapsed.load(std::memory_order_relaxed) +
     559                 :          1 :               (impl_->real_timer.get() * impl_->rate.load(std::memory_order_relaxed))) /
     560                 :          1 :              1000U;
     561                 :            :     }
     562         [ +  + ]:          4 :   } else if (impl_->status.load(std::memory_order_relaxed) == kPaused) {
     563                 :          2 :     return (impl_->real_elapsed.load(std::memory_order_relaxed) +
     564                 :          2 :             ((impl_->real_timer.get() - impl_->pause_elapsed_timer.get() -
     565                 :          2 :               impl_->extra_elapsed.load(std::memory_order_relaxed)) *
     566                 :          2 :              impl_->rate.load(std::memory_order_relaxed))) /
     567                 :          2 :            1000U;
     568                 :            :   } else {
     569                 :          2 :     return 0;
     570                 :            :   }
     571                 :          5 : }
     572                 :            : 
     573                 :          5 : int64_t VCAPReader::get_real_timestamp() const {
     574   [ +  +  +  +  :          9 :   if (impl_->status.load(std::memory_order_relaxed) == kPlaying ||
                   +  + ]
     575                 :          4 :       impl_->status.load(std::memory_order_relaxed) == kPaused) {
     576                 :          6 :     return impl_->real_elapsed.load(std::memory_order_relaxed) / 1000U;
     577                 :            :   } else {
     578                 :          2 :     return 0;
     579                 :            :   }
     580                 :            : }
     581                 :            : 
     582                 :         10 : BagReader::Status VCAPReader::get_status() const { return impl_->status.load(std::memory_order_relaxed); }
     583                 :            : 
     584                 :         28 : const BagReader::Info& VCAPReader::get_info() const { return impl_->info; }
     585                 :            : 
     586                 :         35 : std::vector<SchemaData> VCAPReader::detect_schema() {
     587                 :         35 :   std::vector<SchemaData> schema_list;
     588                 :         35 :   std::unordered_map<std::string, size_t> schema_index_map;
     589                 :            : 
     590         [ +  + ]:         35 :   if (!impl_->info.has_schema) {
     591                 :         24 :     return schema_list;
     592                 :            :   }
     593                 :            : 
     594         [ +  - ]:         11 :   schema_index_map.reserve(impl_->info.url_metas.size());
     595                 :            : 
     596         [ +  + ]:         24 :   for (auto& wrapper_file : impl_->file_list) {
     597   [ +  -  +  + ]:         25 :     for (const auto& [schema_id, schema_ptr] : wrapper_file.reader->schemas()) {
     598                 :            :       (void)schema_id;
     599                 :            : 
     600                 :         12 :       SchemaData schema;
     601         [ +  - ]:         12 :       schema.name = schema_ptr->name;
     602         [ +  - ]:         12 :       schema.encoding = schema_ptr->encoding;
     603                 :         12 :       schema.schema_type = SchemaData::resolve_type(SchemaType::kUnknown, schema.name, schema.encoding);
     604                 :            : 
     605   [ +  -  +  -  :         12 :       if (!schema.name.empty() && !schema_ptr->data.empty()) {
                   +  - ]
     606         [ +  - ]:         12 :         std::string schema_key = schema.name;
     607         [ +  - ]:         12 :         schema_key.push_back('\x1F');
     608         [ +  - ]:         12 :         schema_key.append(SchemaData::convert_type(schema.schema_type));
     609         [ +  - ]:         12 :         auto schema_index_iter = schema_index_map.find(schema_key);
     610                 :            : 
     611         [ +  + ]:         12 :         if (schema_index_iter == schema_index_map.end()) {
     612                 :            :           schema.data =
     613                 :         10 :               Bytes::deep_copy(reinterpret_cast<const uint8_t*>(schema_ptr->data.data()), schema_ptr->data.size());
     614         [ +  - ]:         10 :           schema_index_map.emplace(schema_key, schema_list.size());
     615         [ +  - ]:         10 :           schema_list.emplace_back(std::move(schema));
     616                 :            :         } else {
     617                 :          2 :           auto& current_schema = schema_list[schema_index_iter->second];
     618                 :            : 
     619   [ -  +  -  -  :          2 :           if (current_schema.encoding.empty() && !schema.encoding.empty()) {
                   -  + ]
     620                 :            :             current_schema.encoding = schema.encoding;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     621                 :            :           }
     622                 :            : 
     623         [ -  + ]:          2 :           if (current_schema.data.empty()) {
     624                 :          0 :             current_schema.data = Bytes::deep_copy(
     625                 :            :                 reinterpret_cast<const uint8_t*>(schema_ptr->data.data()),  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     626                 :            :                 schema_ptr->data.size());                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     627                 :            :           }
     628                 :            :         }
     629                 :         12 :       }
     630                 :         25 :     }
     631                 :            :   }
     632                 :            : 
     633                 :         11 :   return schema_list;
     634                 :         35 : }
     635                 :            : 
     636                 :          9 : bool VCAPReader::is_split_mode() const { return impl_->info.split_count > 0; }
     637                 :            : 
     638                 :          4 : int VCAPReader::get_split_index() const { return impl_->split_index.load(std::memory_order_relaxed); }
     639                 :            : 
     640                 :          4 : bool VCAPReader::is_jumping() const { return impl_->jump_flag.load(std::memory_order_relaxed); }
     641                 :            : 
     642                 :         59 : size_t VCAPReader::get_max_task_count() const { return kMaxTaskSize; }
     643                 :            : 
     644                 :         56 : void VCAPReader::on_begin() { MessageLoop::on_begin(); }
     645                 :            : 
     646                 :         56 : void VCAPReader::on_end() { MessageLoop::on_end(); }
     647                 :            : 
     648                 :         30 : void VCAPReader::update_status(Status status) {
     649                 :         30 :   bool has_changed = false;
     650                 :            : 
     651         [ +  + ]:         30 :   if (status == kStopped) {
     652         [ +  + ]:         11 :     if (impl_->status.load(std::memory_order_relaxed) != kStopped) {
     653                 :         10 :       impl_->status.store(kStopped, std::memory_order_relaxed);
     654                 :         10 :       has_changed = true;
     655                 :            :     }
     656         [ +  + ]:         19 :   } else if (status == kPaused) {
     657         [ +  + ]:          5 :     if (impl_->status.load(std::memory_order_relaxed) != kPaused) {
     658                 :          4 :       impl_->status.store(kPaused, std::memory_order_relaxed);
     659                 :          4 :       has_changed = true;
     660                 :            :     }
     661         [ +  - ]:         14 :   } else if (status == kPlaying) {
     662         [ +  + ]:         14 :     if (impl_->status.load(std::memory_order_relaxed) != kPlaying) {
     663                 :         13 :       impl_->status.store(kPlaying, std::memory_order_relaxed);
     664                 :         13 :       has_changed = true;
     665                 :            :     }
     666                 :            :   }
     667                 :            : 
     668         [ +  + ]:         30 :   if (has_changed) {
     669         [ +  + ]:         27 :     if VLIKELY (impl_->status_callback) {
     670                 :         17 :       impl_->status_callback(impl_->status.load(std::memory_order_relaxed));
     671                 :            :     }
     672                 :            :   }
     673                 :         30 : }
     674                 :            : 
     675                 :         61 : void VCAPReader::do_stop() {
     676                 :            :   {
     677         [ +  - ]:         61 :     std::unique_lock lock(impl_->mtx);
     678                 :         61 :     impl_->stop_flag.store(true, std::memory_order_relaxed);
     679                 :         61 :     impl_->pause_flag.store(false, std::memory_order_relaxed);
     680                 :         61 :     impl_->pause_next_flag.store(false, std::memory_order_relaxed);
     681                 :         61 :     impl_->jump_flag.store(false, std::memory_order_relaxed);
     682                 :         61 :   }
     683                 :            : 
     684                 :         61 :   impl_->cv.notify_one();
     685                 :         61 : }
     686                 :            : 
     687                 :          4 : void VCAPReader::do_pause() {
     688         [ +  - ]:          4 :   std::unique_lock lock(impl_->mtx);
     689                 :            : 
     690         [ +  - ]:          4 :   while (impl_->pause_flag.load(std::memory_order_relaxed)) {
     691                 :          4 :     impl_->pause_elapsed_timer.restart();
     692         [ +  - ]:          4 :     update_status(kPaused);
     693                 :            : 
     694                 :          4 :     impl_->cv.wait(lock, [this]() -> bool {
     695   [ +  +  +  + ]:         19 :       return impl_->stop_flag.load(std::memory_order_relaxed) || !impl_->pause_flag.load(std::memory_order_relaxed) ||
     696         [ +  - ]:          8 :              impl_->pause_next_flag.load(std::memory_order_relaxed) ||
     697   [ +  -  -  + ]:         17 :              impl_->jump_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
     698                 :            :     });
     699                 :            : 
     700                 :          4 :     impl_->pause_elapsed.fetch_add(impl_->pause_elapsed_timer.get(), std::memory_order_relaxed);
     701                 :            : 
     702                 :            :     {
     703         [ +  - ]:          4 :       std::lock_guard time_lock(impl_->time_mtx);
     704                 :          4 :       impl_->real_timer.restart();
     705                 :            : 
     706         [ -  + ]:          8 :       if (impl_->offset_elapsed.load(std::memory_order_relaxed) > 0) {
     707                 :            :         impl_->real_elapsed.fetch_add(                                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     708                 :            :             (impl_->offset_timer.get() - impl_->pause_elapsed_timer.get()) *  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     709                 :            :                 impl_->rate.load(std::memory_order_relaxed),                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     710                 :            :             std::memory_order_relaxed);
     711                 :            :       }
     712                 :          4 :     }
     713                 :            : 
     714         [ +  - ]:          4 :     update_status(kPlaying);
     715                 :            : 
     716         [ +  - ]:          4 :     if (impl_->pause_next_flag.load(std::memory_order_relaxed)) {
     717                 :          8 :       impl_->pause_elapsed.fetch_sub(impl_->offset_elapsed.load(std::memory_order_relaxed), std::memory_order_relaxed);
     718                 :          4 :       break;
     719                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     720                 :            :     } else if (impl_->offset_elapsed.load(std::memory_order_relaxed) > 0) {
     721                 :            :       impl_->offset_timer.restart();
     722                 :            : 
     723                 :            :       impl_->cv.wait_for(lock, std::chrono::microseconds(impl_->offset_elapsed.load(std::memory_order_relaxed)),
     724                 :            :                          [this]() -> bool {
     725                 :            :                            return impl_->stop_flag.load(std::memory_order_relaxed) ||
     726                 :            :                                   impl_->pause_flag.load(std::memory_order_relaxed) ||
     727                 :            :                                   impl_->pause_next_flag.load(std::memory_order_relaxed) ||
     728                 :            :                                   impl_->jump_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
     729                 :            :                          });
     730                 :            : 
     731                 :            :       if VUNLIKELY (impl_->pause_flag.load(std::memory_order_relaxed)) {
     732                 :            :         impl_->offset_elapsed.fetch_sub(impl_->offset_timer.get(), std::memory_order_relaxed);
     733                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     734                 :            :       } else {
     735                 :            :         impl_->offset_elapsed.store(0, std::memory_order_relaxed);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     736                 :            :       }
     737                 :            :     }
     738                 :            :   }
     739                 :          4 : }
     740                 :            : 
     741                 :         87 : bool VCAPReader::prepare_file(void* file) {
     742                 :         87 :   auto* wrapper_file = static_cast<Impl::WrapperFile*>(file);
     743                 :            : 
     744                 :         87 :   wrapper_file->has_completed = true;
     745                 :            : 
     746                 :         87 :   auto& reader = wrapper_file->reader;
     747                 :            : 
     748         [ -  + ]:         87 :   if VUNLIKELY (!reader) {
     749                 :            :     wrapper_file->has_completed = false;                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     750                 :            :     CLOG_F("VCAPReader: Mcap [%s] reader is nullptr.", wrapper_file->path.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     751                 :            :     return false;                                                                    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     752                 :            :   }
     753                 :            : 
     754   [ +  -  -  + ]:         87 :   if VUNLIKELY (!reader->header()) {
     755                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     756                 :            :     wrapper_file->has_completed = false;
     757                 :            :     CLOG_F("VCAPReader: Mcap [%s] reader header is nullptr.", wrapper_file->path.c_str());
     758                 :            :     return false;
     759                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     760                 :            :   }
     761                 :            : 
     762   [ +  -  -  + ]:         87 :   if VUNLIKELY (reader->header()->profile != "vlink") {
     763                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     764                 :            :     wrapper_file->has_completed = false;
     765                 :            :     CLOG_F("VCAPReader: Mcap [%s] profile is %s, not valid.", wrapper_file->path.c_str(),
     766                 :            :            reader->header()->profile.c_str());
     767                 :            :     return false;
     768                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     769                 :            :   }
     770                 :            : 
     771                 :         87 :   mcap::Status status;
     772                 :            : 
     773         [ +  - ]:         87 :   status = reader->readSummary(mcap::ReadSummaryMethod::NoFallbackScan);
     774                 :            : 
     775         [ -  + ]:         87 :   if VUNLIKELY (!status.ok()) {
     776                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     777                 :            :     wrapper_file->has_completed = false;
     778                 :            : 
     779                 :            :     if (impl_->try_to_fix) {
     780                 :            :       CLOG_E("VCAPReader: Failed to read summary, error = %s. Trying to fix.", status.message.c_str());
     781                 :            :       status = reader->readSummary(mcap::ReadSummaryMethod::AllowFallbackScan);
     782                 :            : 
     783                 :            :       if VUNLIKELY (!status.ok()) {
     784                 :            :         CLOG_F("VCAPReader: Failed to read summary, error = %s.", status.message.c_str());
     785                 :            :         return false;
     786                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     787                 :            :       }
     788                 :            : 
     789                 :            :     } else {
     790                 :            :       CLOG_F("VCAPReader: Failed to read summary, error = %s.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     791                 :            :              status.message.c_str());                            // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     792                 :            :       return false;                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     793                 :            :     }
     794                 :            :   }
     795                 :            : 
     796         [ +  - ]:         87 :   const auto& meta_index = reader->metadataIndexes();
     797         [ +  - ]:         87 :   const auto& statistics = reader->statistics();
     798                 :            : 
     799         [ -  + ]:         87 :   if (!statistics.has_value()) {
     800                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     801                 :            :     wrapper_file->has_completed = false;
     802                 :            :     CLOG_F("VCAPReader: Mcap [%s] cannot find statistics.", wrapper_file->path.c_str());
     803                 :            :     return false;
     804                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     805                 :            :   }
     806                 :            : 
     807   [ +  -  +  + ]:         87 :   for (const auto& [schema_id, schema_ptr] : reader->schemas()) {
     808                 :            :     (void)schema_id;
     809                 :            : 
     810   [ +  -  +  -  :         18 :     if (schema_ptr && !schema_ptr->data.empty()) {
                   +  - ]
     811                 :         18 :       wrapper_file->has_schema = true;
     812                 :         18 :       impl_->info.has_schema = true;
     813                 :         18 :       break;
     814                 :            :     }
     815                 :         87 :   }
     816                 :            : 
     817                 :            :   // read header
     818                 :            :   {
     819   [ +  -  +  - ]:         87 :     auto header_iter = meta_index.find("VLinkHeader");
     820                 :            : 
     821         [ -  + ]:         87 :     if VUNLIKELY (header_iter == meta_index.end()) {
     822                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     823                 :            :       wrapper_file->has_completed = false;
     824                 :            :       CLOG_F("VCAPReader: Mcap [%s] cannot find header.", wrapper_file->path.c_str());
     825                 :            :       return false;
     826                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     827                 :            :     }
     828                 :            : 
     829                 :         87 :     const auto& header_index = header_iter->second;
     830                 :            : 
     831                 :            :     mcap::Record header_record;
     832                 :            : 
     833   [ +  -  +  - ]:         87 :     status = mcap::McapReader::ReadRecord(*reader->dataSource(), header_index.offset, &header_record);
     834                 :            : 
     835         [ -  + ]:         87 :     if VUNLIKELY (!status.ok()) {
     836                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     837                 :            :       wrapper_file->has_completed = false;
     838                 :            :       CLOG_F("VCAPReader: Failed to read header record for index, error = %s.", status.message.c_str());
     839                 :            :       return false;
     840                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     841                 :            :     }
     842                 :            : 
     843                 :         87 :     mcap::Metadata header_meta_data;
     844                 :            : 
     845         [ +  - ]:         87 :     status = mcap::McapReader::ParseMetadata(header_record, &header_meta_data);
     846                 :            : 
     847         [ -  + ]:         87 :     if VUNLIKELY (!status.ok()) {
     848                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     849                 :            :       wrapper_file->has_completed = false;
     850                 :            :       CLOG_F("VCAPReader: Failed to parse header meta data, error = %s.", status.message.c_str());
     851                 :            :       return false;
     852                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     853                 :            :     }
     854                 :            : 
     855   [ +  -  +  - ]:         87 :     auto& tag_str = header_meta_data.metadata["tag"];
     856   [ +  -  +  - ]:         87 :     auto& version_str = header_meta_data.metadata["version"];
     857   [ +  -  +  - ]:         87 :     auto& compress_str = header_meta_data.metadata["compress"];
     858   [ +  -  +  - ]:         87 :     auto& process_str = header_meta_data.metadata["process"];
     859   [ +  -  +  - ]:         87 :     auto& date_str = header_meta_data.metadata["date"];
     860   [ +  -  +  - ]:         87 :     auto& start_timestamp_str = header_meta_data.metadata["start_timestamp"];
     861   [ +  -  +  - ]:         87 :     auto& timezone_str = header_meta_data.metadata["timezone"];
     862                 :            : 
     863         [ -  + ]:         87 :     if VUNLIKELY (version_str.empty()) {
     864                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     865                 :            :       wrapper_file->has_completed = false;
     866                 :            : 
     867                 :            :       if (impl_->read_only) {
     868                 :            :         CLOG_E("VCAPReader: Mcap [%s] cannot find version in header.", wrapper_file->path.c_str());
     869                 :            :       }
     870                 :            : 
     871                 :            :       version_str = "0.0.0";
     872                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     873                 :            :     } else {
     874                 :         87 :       auto version = Version::from_string(version_str);
     875                 :            : 
     876         [ +  + ]:         87 :       if VUNLIKELY (!version.is_valid()) {
     877                 :          1 :         wrapper_file->has_completed = false;
     878                 :            : 
     879         [ -  + ]:          1 :         if (impl_->read_only) {
     880                 :            :           CLOG_E("VCAPReader: Mcap [%s] header version is invalid.",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     881                 :            :                  wrapper_file->path.c_str());                         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     882                 :            :         }
     883                 :            :       } else {
     884         [ +  + ]:         86 :         if VUNLIKELY (version.major != VLINK_VERSION_MAJOR) {
     885                 :          1 :           wrapper_file->has_completed = false;
     886   [ +  -  -  + ]:          2 :           VLOG_F("VCAPReader: Mcap version is incompatible.");
     887                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     888                 :            :         }
     889                 :            :       }
     890                 :            :     }
     891                 :            : 
     892         [ -  + ]:         86 :     if VUNLIKELY (compress_str.empty()) {
     893                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     894                 :            :       wrapper_file->has_completed = false;
     895                 :            : 
     896                 :            :       if (impl_->read_only) {
     897                 :            :         CLOG_E("VCAPReader: Mcap [%s] cannot find compress in header.", wrapper_file->path.c_str());
     898                 :            :       }
     899                 :            : 
     900                 :            :       compress_str = "Unknown";
     901                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     902                 :            :     }
     903                 :            : 
     904         [ -  + ]:         86 :     if VUNLIKELY (process_str.empty()) {
     905                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     906                 :            :       wrapper_file->has_completed = false;
     907                 :            : 
     908                 :            :       if (impl_->read_only) {
     909                 :            :         CLOG_E("VCAPReader: Mcap [%s] cannot find process in header.", wrapper_file->path.c_str());
     910                 :            :       }
     911                 :            : 
     912                 :            :       process_str = "Unknown";
     913                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     914                 :            :     }
     915                 :            : 
     916         [ -  + ]:         86 :     if VUNLIKELY (date_str.empty()) {
     917                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     918                 :            :       wrapper_file->has_completed = false;
     919                 :            : 
     920                 :            :       if (impl_->read_only) {
     921                 :            :         CLOG_E("VCAPReader: Mcap [%s] cannot find date in header.", wrapper_file->path.c_str());
     922                 :            :       }
     923                 :            : 
     924                 :            :       date_str = "Unknown";
     925                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     926                 :            :     }
     927                 :            : 
     928         [ -  + ]:         86 :     if VUNLIKELY (timezone_str.empty()) {
     929                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     930                 :            :       wrapper_file->has_completed = false;
     931                 :            : 
     932                 :            :       if (impl_->read_only) {
     933                 :            :         CLOG_E("VCAPReader: Mcap [%s] cannot find timezone in header.", wrapper_file->path.c_str());
     934                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     935                 :            :       }
     936                 :            :     }
     937                 :            : 
     938         [ -  + ]:         86 :     if (tag_str.empty()) {
     939                 :            :       tag_str = "Empty";  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     940                 :            :     }
     941                 :            : 
     942         [ +  - ]:         86 :     impl_->info.tag_name = tag_str;
     943         [ +  - ]:         86 :     impl_->info.version = version_str;
     944         [ +  - ]:         86 :     impl_->info.storage_type = "vcap";
     945                 :         86 :     impl_->info.message_count = statistics->messageCount;
     946         [ +  - ]:         86 :     impl_->info.time_accuracy = "MicroSecond";
     947         [ +  - ]:         86 :     impl_->info.compression_type = compress_str;
     948         [ +  - ]:         86 :     impl_->info.process_name = process_str;
     949         [ +  - ]:         86 :     impl_->info.date_time = date_str;
     950                 :            : 
     951                 :            :     try {
     952         [ +  + ]:         86 :       impl_->info.start_timestamp = std::stoll(start_timestamp_str);
     953         [ -  + ]:          1 :     } catch (std::exception&) {
     954                 :          1 :       impl_->info.start_timestamp = Helpers::convert_date_to_timestamp(impl_->info.date_time) / 1000'000;
     955                 :          1 :     }
     956                 :            : 
     957         [ +  + ]:         86 :     if VUNLIKELY (impl_->info.start_timestamp < 0) {
     958                 :          1 :       impl_->info.start_timestamp = 0;
     959                 :            : 
     960         [ -  + ]:          1 :       if (impl_->read_only) {
     961                 :            :         VLOG_E("VCAPReader: Invalid start_timestamp_ns.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     962                 :            :       }
     963                 :            :     }
     964                 :            : 
     965                 :         86 :     int64_t timestamp_diff = 0;
     966                 :            : 
     967                 :         86 :     timestamp_diff = static_cast<int64_t>(statistics->messageStartTime / 1000'000) - impl_->info.start_timestamp;
     968                 :            : 
     969         [ +  + ]:         86 :     if (timestamp_diff < 0) {
     970                 :          3 :       timestamp_diff = 0;
     971                 :          3 :       impl_->info.start_timestamp = static_cast<int64_t>(statistics->messageStartTime / 1000'000);
     972                 :            :     }
     973                 :            : 
     974                 :         86 :     impl_->info.blank_duration = timestamp_diff;
     975                 :            : 
     976                 :         86 :     timestamp_diff = static_cast<int64_t>(statistics->messageEndTime / 1000'000) - impl_->info.start_timestamp;
     977                 :            : 
     978         [ -  + ]:         86 :     if (timestamp_diff < 0) {
     979                 :            :       timestamp_diff = 0;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     980                 :            :     }
     981                 :            : 
     982                 :         86 :     impl_->info.total_duration = timestamp_diff;
     983                 :            : 
     984                 :            :     try {
     985         [ +  - ]:         86 :       impl_->info.timezone = std::stoi(timezone_str);
     986                 :            :     } catch (std::exception&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     987                 :            :       impl_->info.timezone = 0;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     988                 :            :     }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     989         [ +  - ]:         87 :   }
     990                 :            : 
     991                 :            :   // read channel
     992                 :            :   {
     993                 :         86 :     int channel = 0;
     994                 :         86 :     std::string channel_str;
     995                 :            :     mcap::Record channel_record;
     996                 :            : 
     997         [ +  - ]:         86 :     url_ser_map().clear();
     998         [ +  - ]:         86 :     url_schema_type_map().clear();
     999                 :         86 :     impl_->info.url_metas.clear();
    1000                 :         86 :     impl_->raw_url_metas.clear();
    1001                 :            : 
    1002                 :         86 :     impl_->info.total_raw_size = 0;
    1003                 :            : 
    1004         [ +  + ]:        286 :     for (const auto& [name, index] : meta_index) {
    1005         [ +  + ]:        200 :       if (!Helpers::has_startwith(name, "VLinkChannel_")) {
    1006                 :         86 :         continue;
    1007                 :            :       }
    1008                 :            : 
    1009         [ +  - ]:        114 :       channel_str = name;
    1010   [ +  -  +  - ]:        114 :       Helpers::replace_string(channel_str, "VLinkChannel_", "");
    1011                 :            : 
    1012                 :            :       try {
    1013         [ +  - ]:        114 :         channel = std::stoi(channel_str);
    1014                 :            :       } catch (std::exception&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1015                 :            :         continue;                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1016                 :            :       }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1017                 :            : 
    1018         [ +  - ]:        114 :       auto channel_count_iter = statistics->channelMessageCounts.find(channel);
    1019                 :            : 
    1020         [ -  + ]:        114 :       if VUNLIKELY (channel_count_iter == statistics->channelMessageCounts.end()) {
    1021                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1022                 :            :         wrapper_file->has_completed = false;
    1023                 :            : 
    1024                 :            :         if (impl_->read_only) {
    1025                 :            :           CLOG_E("VCAPReader: Mcap [%s] cannot read statistics in channel.", wrapper_file->path.c_str());
    1026                 :            :         }
    1027                 :            : 
    1028                 :            :         continue;
    1029                 :            :       }
    1030                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1031                 :            : 
    1032                 :        114 :       auto channel_msg_count = channel_count_iter->second;
    1033                 :            : 
    1034         [ +  - ]:        114 :       const auto& channel_ptr = reader->channel(channel);
    1035                 :            : 
    1036         [ -  + ]:        114 :       if VUNLIKELY (!channel_ptr) {
    1037                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1038                 :            :         wrapper_file->has_completed = false;
    1039                 :            : 
    1040                 :            :         if (impl_->read_only) {
    1041                 :            :           CLOG_E("VCAPReader: Mcap [%s] cannot find ptr in channel.", wrapper_file->path.c_str());
    1042                 :            :         }
    1043                 :            : 
    1044                 :            :         continue;
    1045                 :            :       }
    1046                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1047                 :            : 
    1048   [ +  -  +  - ]:        114 :       status = mcap::McapReader::ReadRecord(*reader->dataSource(), index.offset, &channel_record);
    1049                 :            : 
    1050         [ -  + ]:        114 :       if VUNLIKELY (!status.ok()) {
    1051                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1052                 :            :         wrapper_file->has_completed = false;
    1053                 :            : 
    1054                 :            :         if (impl_->read_only) {
    1055                 :            :           CLOG_E("VCAPReader: Failed to read channel record for index, error = %s.", status.message.c_str());
    1056                 :            :         }
    1057                 :            : 
    1058                 :            :         continue;
    1059                 :            :       }
    1060                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1061                 :            : 
    1062                 :        114 :       mcap::Metadata channel_meta_data;
    1063                 :            : 
    1064         [ +  - ]:        114 :       status = mcap::McapReader::ParseMetadata(channel_record, &channel_meta_data);
    1065                 :            : 
    1066         [ -  + ]:        114 :       if VUNLIKELY (!status.ok()) {
    1067                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1068                 :            :         wrapper_file->has_completed = false;
    1069                 :            : 
    1070                 :            :         if (impl_->read_only) {
    1071                 :            :           CLOG_E("VCAPReader: Failed to parse channel meta data, error = %s.", status.message.c_str());
    1072                 :            :         }
    1073                 :            : 
    1074                 :            :         continue;
    1075                 :            :       }
    1076                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1077                 :            : 
    1078                 :        114 :       Info::UrlMeta url_meta;
    1079                 :            : 
    1080   [ +  -  +  - ]:        114 :       auto& index_str = channel_meta_data.metadata["index"];
    1081   [ +  -  +  - ]:        114 :       auto& type_str = channel_meta_data.metadata["type"];
    1082   [ +  -  +  - ]:        114 :       auto& count_str = channel_meta_data.metadata["count"];
    1083   [ +  -  +  - ]:        114 :       auto& size_str = channel_meta_data.metadata["size"];
    1084   [ +  -  +  - ]:        114 :       auto& loss_str = channel_meta_data.metadata["loss"];
    1085   [ +  -  +  - ]:        114 :       auto& freq_str = channel_meta_data.metadata["freq"];
    1086   [ +  -  +  - ]:        114 :       auto ser_iter = channel_meta_data.metadata.find("ser");
    1087   [ +  -  +  - ]:        114 :       auto encoding_iter = channel_meta_data.metadata.find("encoding");
    1088   [ +  -  +  - ]:        114 :       auto action_iter = channel_meta_data.metadata.find("action");
    1089         [ +  - ]:        114 :       const auto& schema_ptr = reader->schema(channel_ptr->schemaId);
    1090                 :            : 
    1091                 :        114 :       int pindex = -1;
    1092                 :            : 
    1093                 :            :       try {
    1094         [ +  - ]:        114 :         pindex = std::stoi(index_str);
    1095                 :            :       } catch (std::exception&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1096                 :            :         pindex = -1;               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1097                 :            :       }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1098                 :            : 
    1099         [ -  + ]:        114 :       if VUNLIKELY (pindex != channel - 1) {
    1100                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1101                 :            :         wrapper_file->has_completed = false;
    1102                 :            : 
    1103                 :            :         if (impl_->read_only) {
    1104                 :            :           CLOG_E("VCAPReader: Mcap [%s] channel index error.", wrapper_file->path.c_str());
    1105                 :            :         }
    1106                 :            : 
    1107                 :            :         continue;
    1108                 :            :       }
    1109                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1110                 :            : 
    1111                 :        114 :       url_meta.valid = true;
    1112                 :        114 :       url_meta.index = pindex;
    1113         [ +  - ]:        114 :       url_meta.url = channel_ptr->topic;
    1114         [ +  - ]:        114 :       url_meta.url_type = type_str;
    1115                 :        114 :       url_meta.schema_type = SchemaType::kUnknown;
    1116                 :            : 
    1117         [ +  - ]:        114 :       if (ser_iter != channel_meta_data.metadata.end()) {
    1118         [ +  - ]:        114 :         url_meta.ser_type = ser_iter->second;
    1119                 :            :       } else if (schema_ptr) {                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1120                 :            :         url_meta.ser_type = schema_ptr->name;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1121                 :            :       }
    1122                 :            : 
    1123         [ +  - ]:        114 :       if (encoding_iter != channel_meta_data.metadata.end()) {
    1124                 :        114 :         url_meta.schema_type =
    1125         [ +  + ]:        132 :             SchemaData::resolve_type(SchemaData::convert_encoding(encoding_iter->second), url_meta.ser_type,
    1126                 :        132 :                                      schema_ptr ? std::string_view(schema_ptr->encoding) : std::string_view{});
    1127                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1128                 :            :       } else if (schema_ptr) {
    1129                 :            :         url_meta.schema_type =
    1130                 :            :             SchemaData::resolve_type(SchemaData::convert_encoding(schema_ptr->encoding), url_meta.ser_type);
    1131                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1132                 :            :       }
    1133                 :            : 
    1134   [ +  +  +  -  :        114 :       if (schema_ptr && !schema_ptr->data.empty()) {
                   +  + ]
    1135                 :         18 :         wrapper_file->has_schema = true;
    1136                 :         18 :         impl_->info.has_schema = true;
    1137                 :            :       }
    1138                 :            : 
    1139                 :            :       try {
    1140         [ +  - ]:        114 :         url_meta.count = static_cast<size_t>(std::stoull(count_str));
    1141                 :            :       } catch (std::exception&) {            // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1142                 :            :         url_meta.count = channel_msg_count;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1143                 :            :       }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1144                 :            : 
    1145                 :            :       try {
    1146         [ +  - ]:        114 :         url_meta.loss = std::stod(loss_str);
    1147                 :            :       } catch (std::exception&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1148                 :            :         url_meta.loss = 0;         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1149                 :            :       }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1150                 :            : 
    1151                 :            :       try {
    1152         [ +  - ]:        114 :         url_meta.size = static_cast<size_t>(std::stoull(size_str));
    1153                 :            :       } catch (std::exception&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1154                 :            :         url_meta.size = 0;         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1155                 :            :       }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1156                 :            : 
    1157                 :            :       try {
    1158         [ +  - ]:        114 :         url_meta.freq = std::stod(freq_str);
    1159                 :            :       } catch (std::exception&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1160                 :            :         url_meta.freq = 0;         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1161                 :            :       }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1162                 :            : 
    1163                 :        114 :       impl_->info.total_raw_size += url_meta.size;
    1164                 :            : 
    1165         [ +  - ]:        114 :       wrapper_file->id_to_url_map.emplace(url_meta.index, url_meta.url);
    1166         [ +  - ]:        114 :       wrapper_file->url_to_id_map.emplace(url_meta.url, url_meta.index);
    1167                 :            : 
    1168   [ +  -  +  - ]:        114 :       url_ser_map().emplace(url_meta.url, url_meta.ser_type);
    1169   [ +  -  +  - ]:        114 :       url_schema_type_map().emplace(url_meta.url, url_meta.schema_type);
    1170                 :            : 
    1171         [ +  - ]:        114 :       if (action_iter != channel_meta_data.metadata.end()) {
    1172         [ +  - ]:        114 :         url_meta.action_type = convert_action(action_iter->second);
    1173         [ +  - ]:        114 :         wrapper_file->channel_action_map.emplace(channel, url_meta.action_type);
    1174                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1175                 :            :       } else if (auto channel_action_iter = channel_ptr->metadata.find("action");
    1176                 :            :                  channel_action_iter != channel_ptr->metadata.end()) {
    1177                 :            :         url_meta.action_type = convert_action(channel_action_iter->second);
    1178                 :            :         wrapper_file->channel_action_map.emplace(channel, url_meta.action_type);
    1179                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1180                 :            :       } else {
    1181                 :            :         wrapper_file->channel_action_map.emplace(channel,                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1182                 :            :                                                  ActionType::kUnknownAction);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1183                 :            :       }
    1184                 :            : 
    1185         [ +  - ]:        114 :       impl_->info.url_metas.emplace_back(std::move(url_meta));
    1186   [ +  -  +  -  :        114 :     }
             +  -  +  - ]
    1187                 :            : 
    1188                 :         86 :     wrapper_file->is_channel_broken = false;
    1189                 :            : 
    1190         [ +  + ]:         86 :     if (impl_->info.url_metas.empty()) {
    1191   [ +  -  -  + ]:          2 :       for (const auto& [id, pchannel] : reader->channels()) {
    1192                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
    1193                 :            :         wrapper_file->is_channel_broken = true;
    1194                 :            : 
    1195                 :            :         auto pschema_ptr = reader->schema(pchannel->schemaId);
    1196                 :            :         auto channel_count_iter = statistics->channelMessageCounts.find(id);
    1197                 :            : 
    1198                 :            :         Info::UrlMeta url_meta;
    1199                 :            : 
    1200                 :            :         url_meta.valid = true;
    1201                 :            :         url_meta.index = id - 1;
    1202                 :            :         url_meta.url = pchannel->topic;
    1203                 :            :         url_meta.url_type = "Event";
    1204                 :            : 
    1205                 :            :         if (pschema_ptr) {
    1206                 :            :           url_meta.ser_type = pschema_ptr->name;
    1207                 :            :           url_meta.schema_type =
    1208                 :            :               SchemaData::resolve_type(SchemaData::convert_encoding(pschema_ptr->encoding), url_meta.ser_type);
    1209                 :            : 
    1210                 :            :           if (!pschema_ptr->data.empty()) {
    1211                 :            :             wrapper_file->has_schema = true;
    1212                 :            :             impl_->info.has_schema = true;
    1213                 :            :           }
    1214                 :            :         }
    1215                 :            : 
    1216                 :            :         if (channel_count_iter != statistics->channelMessageCounts.end()) {
    1217                 :            :           url_meta.count = channel_count_iter->second;
    1218                 :            :         }
    1219                 :            : 
    1220                 :            :         wrapper_file->id_to_url_map.emplace(url_meta.index, url_meta.url);
    1221                 :            :         wrapper_file->url_to_id_map.emplace(url_meta.url, url_meta.index);
    1222                 :            : 
    1223                 :            :         url_ser_map().emplace(url_meta.url, url_meta.ser_type);
    1224                 :            :         url_schema_type_map().emplace(url_meta.url, url_meta.schema_type);
    1225                 :            : 
    1226                 :            :         if (auto action_iter = pchannel->metadata.find("action"); action_iter != pchannel->metadata.end()) {
    1227                 :            :           url_meta.action_type = convert_action(action_iter->second);
    1228                 :            :           wrapper_file->channel_action_map.emplace(id, url_meta.action_type);
    1229                 :            :           // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1230                 :            :         } else {
    1231                 :            :           wrapper_file->channel_action_map.emplace(id, ActionType::kUnknownAction);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1232                 :            :         }
    1233                 :            : 
    1234                 :            :         impl_->info.url_metas.emplace_back(std::move(url_meta));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1235                 :          2 :       }
    1236                 :            :     }
    1237                 :            : 
    1238         [ +  - ]:         86 :     std::sort(impl_->info.url_metas.begin(), impl_->info.url_metas.end());
    1239         [ +  - ]:         86 :     impl_->raw_url_metas = impl_->info.url_metas;
    1240         [ +  - ]:         86 :     rebuild_url_meta_lookup(impl_->info.url_metas);
    1241                 :         86 :   }
    1242                 :            : 
    1243                 :         86 :   impl_->info.has_idx_elapsed = false;
    1244                 :         86 :   impl_->info.has_idx_url = false;
    1245                 :            : 
    1246                 :         86 :   impl_->info.has_completed = wrapper_file->has_completed;
    1247                 :            : 
    1248                 :         86 :   return true;
    1249                 :         87 : }
    1250                 :            : 
    1251                 :         67 : void VCAPReader::open(const std::string& path) {
    1252         [ +  - ]:         67 :   close();
    1253                 :            : 
    1254                 :        175 :   auto to_open = [this](Impl::WrapperFile& wrapper_file) -> bool {
    1255                 :         88 :     mcap::Status status;
    1256                 :            : 
    1257         [ +  - ]:         88 :     wrapper_file.reader = std::make_unique<mcap::McapReader>();
    1258                 :            : 
    1259         [ +  - ]:         88 :     status = wrapper_file.reader->open(wrapper_file.path);
    1260                 :            : 
    1261         [ +  + ]:         88 :     if VUNLIKELY (!status.ok()) {
    1262   [ +  -  -  + ]:          2 :       CLOG_F("VCAPReader: Failed to open vcap, error = %s.", status.message.c_str());
    1263                 :            :       wrapper_file.reader.reset();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1264                 :            :       return false;                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1265                 :            :     }
    1266                 :            : 
    1267   [ +  +  -  + ]:         87 :     if VUNLIKELY (!prepare_file(&wrapper_file)) {
    1268                 :            :       wrapper_file.reader->close();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1269                 :            :       wrapper_file.reader.reset();   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1270                 :            :       return false;                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1271                 :            :     }
    1272                 :            : 
    1273                 :         86 :     return true;
    1274                 :         88 :   };
    1275                 :            : 
    1276         [ +  - ]:         67 :   impl_->path = path;
    1277                 :            : 
    1278                 :         67 :   impl_->total_start_timestamp_ns = -1;
    1279                 :            : 
    1280                 :         67 :   impl_->total_has_completed = true;
    1281                 :            : 
    1282                 :            :   try {
    1283                 :            : #ifdef _WIN32
    1284                 :            :     std::filesystem::path file_path(Helpers::string_to_wstring(path));
    1285                 :            : 
    1286                 :            :     impl_->info.file_name = Helpers::path_to_string(file_path.filename());
    1287                 :            : 
    1288                 :            :     impl_->info.file_size = 0;
    1289                 :            : 
    1290                 :            :     std::string suffix = Helpers::path_to_string(file_path.extension());
    1291                 :            : #else
    1292         [ +  - ]:         67 :     std::filesystem::path file_path(path);
    1293                 :            : 
    1294   [ +  -  +  - ]:         67 :     impl_->info.file_name = file_path.filename().string();
    1295                 :            : 
    1296                 :         67 :     impl_->info.file_size = 0;
    1297                 :            : 
    1298   [ +  -  +  - ]:         67 :     std::string suffix = file_path.extension().string();
    1299                 :            : #endif
    1300                 :            : 
    1301                 :         67 :     std::error_code exists_ec;
    1302                 :            : 
    1303         [ +  + ]:         67 :     if VUNLIKELY (!std::filesystem::exists(file_path, exists_ec)) {
    1304   [ +  -  -  + ]:          2 :       CLOG_F("VCAPReader: Mcap [%s] does not exist.", path.c_str());
    1305                 :            :       return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1306                 :            :     }
    1307                 :            : 
    1308                 :         66 :     std::filesystem::path parent_path;
    1309                 :            : 
    1310                 :            :     try {
    1311         [ +  - ]:         66 :       parent_path = file_path.parent_path();
    1312                 :            :     } catch (std::filesystem::filesystem_error&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1313                 :            :     }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1314                 :            : 
    1315                 :        422 :     std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) { return std::tolower(c); });
    1316                 :            : 
    1317         [ +  + ]:         66 :     if (suffix == ".vcapx") {
    1318                 :            :       try {
    1319                 :         26 :         int64_t blank_duration = -1;
    1320                 :            : 
    1321                 :         26 :         nlohmann::json root_json;
    1322                 :            : 
    1323                 :            :         {
    1324         [ +  - ]:         26 :           std::ifstream filex(file_path);
    1325                 :            : 
    1326         [ +  + ]:         26 :           filex >> root_json;
    1327                 :            : 
    1328         [ +  - ]:         25 :           filex.close();
    1329                 :         26 :         }
    1330                 :            : 
    1331   [ +  -  +  - ]:         25 :         nlohmann::json header_json = root_json["VLinkHeader"];
    1332   [ +  -  +  - ]:         25 :         nlohmann::json urls_json = root_json["VLinkUrls"];
    1333   [ +  -  +  - ]:         25 :         nlohmann::json files_json = root_json["VLinkFiles"];
    1334                 :            : 
    1335                 :         25 :         impl_->info.file_size = 0;
    1336                 :            : 
    1337                 :         25 :         int file_index = 0;
    1338                 :            : 
    1339         [ +  + ]:         25 :         if (!files_json.empty()) {
    1340         [ +  - ]:         24 :           impl_->file_list.reserve(files_json.size());
    1341                 :            : 
    1342                 :         24 :           impl_->info.has_idx_elapsed = true;
    1343                 :         24 :           impl_->info.has_idx_url = true;
    1344                 :         24 :           impl_->info.has_schema = false;
    1345                 :            :         } else {
    1346                 :          1 :           impl_->info.has_idx_elapsed = false;
    1347                 :          1 :           impl_->info.has_idx_url = false;
    1348                 :          1 :           impl_->info.has_schema = false;
    1349                 :            :         }
    1350                 :            : 
    1351                 :         25 :         std::filesystem::path file_db;
    1352                 :         25 :         std::string file_db_str;
    1353                 :            : 
    1354   [ +  -  +  -  :         73 :         for (const auto& file_info : files_json) {
             +  -  +  + ]
    1355                 :            : #ifdef _WIN32
    1356                 :            : 
    1357                 :            :           if (parent_path.empty()) {
    1358                 :            :             file_db = std::filesystem::path(Helpers::string_to_wstring(file_info));
    1359                 :            :           } else {
    1360                 :            :             file_db = parent_path / std::filesystem::path(Helpers::string_to_wstring(file_info));
    1361                 :            :           }
    1362                 :            : #else
    1363                 :            : 
    1364         [ +  + ]:         49 :           if (parent_path.empty()) {
    1365         [ +  - ]:          4 :             file_db = std::filesystem::path(file_info);
    1366                 :            :           } else {
    1367   [ +  -  +  - ]:         45 :             file_db = parent_path / std::filesystem::path(file_info);
    1368                 :            :           }
    1369                 :            : #endif
    1370                 :            : 
    1371         [ +  - ]:         49 :           file_db_str = file_db.string();
    1372                 :            : 
    1373                 :         49 :           std::error_code db_exists_ec;
    1374                 :            : 
    1375         [ +  + ]:         49 :           if VUNLIKELY (!std::filesystem::exists(file_db, db_exists_ec)) {
    1376   [ +  -  -  + ]:          2 :             CLOG_F("VCAPReader: Mcap [%s] does not exist.", file_db_str.c_str());
    1377                 :            :             return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1378                 :            :           }
    1379                 :            : 
    1380         [ +  - ]:         48 :           Impl::WrapperFile wrapper_file;
    1381         [ +  - ]:         48 :           wrapper_file.path = file_db_str;
    1382                 :         48 :           wrapper_file.index = file_index;
    1383                 :            : 
    1384   [ +  -  -  + ]:         48 :           if VUNLIKELY (!to_open(wrapper_file)) {
    1385                 :            :             CLOG_W("VCAPReader: Skipping invalid vcap [%s].", file_db_str.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1386                 :            :             continue;                                                                // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1387                 :            :           }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1388                 :            : 
    1389         [ +  - ]:         48 :           if (!wrapper_file.has_idx_elapsed) {
    1390                 :         48 :             impl_->info.has_idx_elapsed = false;
    1391                 :            :           }
    1392                 :            : 
    1393         [ +  - ]:         48 :           if (!wrapper_file.has_idx_url) {
    1394                 :         48 :             impl_->info.has_idx_url = false;
    1395                 :            :           }
    1396                 :            : 
    1397         [ +  + ]:         48 :           if (wrapper_file.has_schema) {
    1398                 :          2 :             impl_->info.has_schema = true;
    1399                 :            :           }
    1400                 :            : 
    1401                 :         48 :           std::error_code db_size_ec;
    1402                 :         48 :           std::uintmax_t file_size = std::filesystem::file_size(file_db, db_size_ec);
    1403                 :            : 
    1404         [ -  + ]:         48 :           if VUNLIKELY (db_size_ec) {
    1405                 :            :             CLOG_W("VCAPReader: file_size failed for [%s]: %s.", file_db_str.c_str(),  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1406                 :            :                    db_size_ec.message().c_str());                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1407                 :            :             file_size = 0;                                                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1408                 :            :           }
    1409                 :            : 
    1410                 :         48 :           impl_->info.file_size += file_size;
    1411                 :            : 
    1412                 :         48 :           wrapper_file.start_timestamp_ns = impl_->info.start_timestamp * 1000'000;
    1413                 :         48 :           wrapper_file.begin = impl_->info.blank_duration;
    1414                 :         48 :           wrapper_file.end = impl_->info.total_duration;
    1415                 :            : 
    1416         [ +  + ]:         48 :           if (impl_->total_start_timestamp_ns < 0) {
    1417                 :         23 :             impl_->total_start_timestamp_ns = wrapper_file.start_timestamp_ns;
    1418                 :            :           }
    1419                 :            : 
    1420         [ -  + ]:         48 :           if (!wrapper_file.has_completed) {
    1421                 :            :             impl_->total_has_completed = false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1422                 :            :           }
    1423                 :            : 
    1424         [ +  + ]:         48 :           if (blank_duration < 0) {
    1425                 :         23 :             blank_duration = impl_->info.blank_duration;
    1426                 :            :           }
    1427                 :            : 
    1428         [ +  - ]:         48 :           impl_->file_list.emplace_back(std::move(wrapper_file));
    1429                 :         48 :           ++file_index;
    1430         [ +  - ]:         48 :         }
    1431                 :            : 
    1432                 :         24 :         impl_->info.split_count = impl_->file_list.size();
    1433                 :            : 
    1434         [ +  + ]:         24 :         if VUNLIKELY (impl_->file_list.empty()) {
    1435   [ +  -  -  + ]:          2 :           VLOG_F("VCAPReader: DB list is empty.");
    1436                 :            :           return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1437                 :            :         }
    1438                 :            : 
    1439   [ +  -  +  - ]:         23 :         int version_major = header_json["major"];
    1440   [ +  -  +  - ]:         23 :         int version_minor = header_json["minor"];
    1441   [ +  -  +  - ]:         23 :         int version_patch = header_json["patch"];
    1442                 :            : 
    1443                 :         23 :         impl_->info.version =
    1444   [ +  -  +  -  :         46 :             std::to_string(version_major) + "." + std::to_string(version_minor) + "." + std::to_string(version_patch);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
    1445         [ +  - ]:         23 :         impl_->info.storage_type = "vcap";
    1446   [ +  -  +  - ]:         23 :         impl_->info.message_count = header_json["count"];
    1447   [ +  -  +  - ]:         23 :         impl_->info.total_duration = header_json["duration"];
    1448                 :         23 :         impl_->info.total_duration /= 1000U;
    1449   [ +  -  +  - ]:         23 :         impl_->info.time_accuracy = header_json["accuracy"];
    1450   [ +  -  +  - ]:         23 :         impl_->info.compression_type = header_json["compress"];
    1451   [ +  -  +  - ]:         23 :         impl_->info.process_name = header_json["process"];
    1452   [ +  -  +  - ]:         23 :         impl_->info.date_time = header_json["date"];
    1453                 :            : 
    1454   [ +  -  +  + ]:         23 :         if (header_json.contains("start_timestamp")) {
    1455   [ +  -  +  - ]:         22 :           impl_->info.start_timestamp = header_json["start_timestamp"];
    1456                 :            :         } else {
    1457                 :          1 :           impl_->info.start_timestamp = Helpers::convert_date_to_timestamp(impl_->info.date_time) / 1000'000;
    1458                 :            :         }
    1459                 :            : 
    1460         [ +  + ]:         23 :         if VUNLIKELY (impl_->info.start_timestamp < 0) {
    1461                 :          1 :           impl_->info.start_timestamp = 0;
    1462                 :            : 
    1463         [ +  - ]:          1 :           if (impl_->read_only) {
    1464   [ +  -  +  - ]:          2 :             VLOG_E("VCAPReader: Invalid start_timestamp.");
    1465                 :            :           }
    1466                 :            :         }
    1467                 :            : 
    1468   [ +  -  +  + ]:         23 :         if (header_json.contains("tag")) {
    1469   [ +  -  +  - ]:         22 :           impl_->info.tag_name = header_json["tag"];
    1470                 :            :         } else {
    1471         [ +  - ]:          1 :           impl_->info.tag_name = "Empty";
    1472                 :            :         }
    1473                 :            : 
    1474   [ +  -  +  + ]:         23 :         if (header_json.contains("complete")) {
    1475   [ +  -  +  - ]:         22 :           impl_->info.has_completed = header_json["complete"];
    1476                 :            :         } else {
    1477                 :          1 :           impl_->info.has_completed = true;
    1478                 :            :         }
    1479                 :            : 
    1480   [ +  -  +  + ]:         23 :         if (header_json.contains("timezone")) {
    1481   [ +  -  +  - ]:         22 :           impl_->info.timezone = header_json["timezone"];
    1482                 :            :         } else {
    1483                 :          1 :           impl_->info.timezone = 480;
    1484                 :            :         }
    1485                 :            : 
    1486   [ +  -  +  + ]:         23 :         if (header_json.contains("split_by_size")) {
    1487   [ +  -  +  - ]:         22 :           impl_->info.split_by_size = header_json["split_by_size"];
    1488                 :            :         }
    1489                 :            : 
    1490   [ +  -  +  + ]:         23 :         if (header_json.contains("split_by_time")) {
    1491   [ +  -  +  - ]:         22 :           impl_->info.split_by_time = header_json["split_by_time"];
    1492                 :            :         }
    1493                 :            : 
    1494                 :         23 :         impl_->info.blank_duration = blank_duration;
    1495                 :            : 
    1496   [ +  +  +  + ]:         49 :         if (impl_->info.compression_type.empty() || impl_->info.compression_type == "None" ||
    1497   [ +  -  +  +  :         49 :             impl_->info.compression_type == "NONE" || impl_->info.compression_type == "none") {
                   +  + ]
    1498                 :         22 :           impl_->enable_compress = false;
    1499                 :            :         } else {
    1500                 :          1 :           impl_->enable_compress = true;
    1501                 :            :         }
    1502                 :            : 
    1503         [ +  + ]:         23 :         if VUNLIKELY (impl_->info.time_accuracy != "MicroSecond") {
    1504   [ +  -  -  + ]:          2 :           VLOG_F("VCAPReader: MCAP accuracy is not supported.");
    1505                 :            :           return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1506                 :            :         }
    1507                 :            : 
    1508         [ +  - ]:         22 :         url_ser_map().clear();
    1509         [ +  - ]:         22 :         url_schema_type_map().clear();
    1510                 :         22 :         impl_->info.url_metas.clear();
    1511                 :         22 :         impl_->raw_url_metas.clear();
    1512                 :            : 
    1513         [ +  - ]:         22 :         impl_->info.url_metas.reserve(urls_json.size());
    1514                 :            : 
    1515                 :         22 :         impl_->info.total_raw_size = 0;
    1516                 :            : 
    1517   [ +  -  +  -  :         44 :         for (const auto& url_info : urls_json) {
             +  -  +  + ]
    1518                 :         22 :           Info::UrlMeta url_meta;
    1519                 :            : 
    1520                 :         22 :           url_meta.valid = true;
    1521   [ +  -  +  - ]:         22 :           url_meta.index = url_info["index"];
    1522   [ +  -  +  - ]:         22 :           url_meta.url = url_info["url"];
    1523   [ +  -  +  - ]:         22 :           url_meta.url_type = url_info["type"];
    1524                 :            : 
    1525   [ +  -  +  + ]:         22 :           if (url_info.contains("action")) {
    1526   [ +  -  +  -  :         21 :             url_meta.action_type = convert_action(url_info["action"].get<std::string>());
                   +  - ]
    1527                 :            :           }
    1528                 :            : 
    1529   [ +  -  +  - ]:         22 :           url_meta.ser_type = url_info["ser"];
    1530                 :            : 
    1531   [ +  -  +  + ]:         22 :           if (url_info.contains("encoding")) {
    1532                 :         42 :             url_meta.schema_type = SchemaData::resolve_type(
    1533   [ +  -  +  - ]:         42 :                 SchemaData::convert_encoding(url_info["encoding"].get<std::string>()), url_meta.ser_type);
    1534                 :            :           } else {
    1535                 :          1 :             url_meta.schema_type = SchemaType::kUnknown;
    1536                 :            :           }
    1537                 :            : 
    1538   [ +  -  +  - ]:         22 :           url_meta.count = url_info["count"];
    1539   [ +  -  +  - ]:         22 :           url_meta.loss = url_info["loss"];
    1540                 :            : 
    1541   [ +  -  +  + ]:         22 :           if (url_info.contains("size")) {
    1542   [ +  -  +  - ]:         21 :             url_meta.size = url_info["size"];
    1543                 :            :           }
    1544                 :            : 
    1545   [ +  -  +  + ]:         22 :           if (url_info.contains("freq")) {
    1546   [ +  -  +  - ]:         21 :             url_meta.freq = url_info["freq"];
    1547                 :            :           }
    1548                 :            : 
    1549                 :         22 :           impl_->info.total_raw_size += url_meta.size;
    1550                 :            : 
    1551   [ +  -  +  - ]:         22 :           url_ser_map().emplace(url_meta.url, url_meta.ser_type);
    1552   [ +  -  +  - ]:         22 :           url_schema_type_map().emplace(url_meta.url, url_meta.schema_type);
    1553         [ +  - ]:         22 :           impl_->info.url_metas.emplace_back(std::move(url_meta));
    1554                 :         22 :         }
    1555                 :            : 
    1556         [ +  - ]:         22 :         std::sort(impl_->info.url_metas.begin(), impl_->info.url_metas.end());
    1557         [ +  - ]:         22 :         impl_->raw_url_metas = impl_->info.url_metas;
    1558         [ +  - ]:         22 :         rebuild_url_meta_lookup(impl_->info.url_metas);
    1559   [ +  -  +  -  :         45 :       } catch (nlohmann::json::exception& e) {
          +  -  +  -  +  
             -  +  -  +  
                      + ]
    1560   [ +  -  -  + ]:          2 :         VLOG_F("VCAPReader: JSON parse error, ", e.what(), ".");
    1561                 :            :         return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1562                 :          1 :       }
    1563                 :            :     } else {
    1564         [ +  - ]:         40 :       Impl::WrapperFile wrapper_file;
    1565         [ +  - ]:         40 :       wrapper_file.path = impl_->path;
    1566                 :            : 
    1567   [ +  +  -  + ]:         40 :       if VUNLIKELY (!to_open(wrapper_file)) {
    1568                 :            :         CLOG_F("VCAPReader: Failed to prepare vcap [%s].", path.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1569                 :            :         return;                                                            // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1570                 :            :       }
    1571                 :            : 
    1572                 :         38 :       impl_->info.file_size = 0;
    1573                 :            : 
    1574                 :         38 :       std::error_code single_size_ec;
    1575                 :         38 :       std::uintmax_t file_size = std::filesystem::file_size(file_path, single_size_ec);
    1576                 :            : 
    1577         [ -  + ]:         38 :       if VUNLIKELY (single_size_ec) {
    1578                 :            :         CLOG_W("VCAPReader: file_size failed for [%s]: %s.", path.c_str(),  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1579                 :            :                single_size_ec.message().c_str());                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1580                 :            :         file_size = 0;                                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1581                 :            :       }
    1582                 :            : 
    1583                 :         38 :       impl_->info.file_size += file_size;
    1584                 :            : 
    1585                 :         38 :       wrapper_file.start_timestamp_ns = impl_->info.start_timestamp * 1000'000;
    1586                 :         38 :       wrapper_file.begin = impl_->info.blank_duration;
    1587                 :         38 :       wrapper_file.end = impl_->info.total_duration;
    1588                 :            : 
    1589         [ +  - ]:         38 :       if (impl_->total_start_timestamp_ns < 0) {
    1590                 :         38 :         impl_->total_start_timestamp_ns = wrapper_file.start_timestamp_ns;
    1591                 :            :       }
    1592                 :            : 
    1593         [ +  + ]:         38 :       if (!wrapper_file.has_completed) {
    1594                 :          1 :         impl_->total_has_completed = false;
    1595                 :            :       }
    1596                 :            : 
    1597         [ +  - ]:         38 :       impl_->file_list.emplace_back(std::move(wrapper_file));
    1598         [ +  - ]:         40 :     }
    1599   [ +  -  +  -  :         87 :   } catch (std::filesystem::filesystem_error& e) {
             +  -  +  - ]
    1600                 :            :     VLOG_F("VCAPReader: Filesystem error, ", e.what(), ".");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1601                 :            :     return;                                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1602                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1603                 :            : }
    1604                 :            : 
    1605                 :        127 : void VCAPReader::close() {
    1606                 :        127 :   impl_->cursor_iter.reset();
    1607                 :        127 :   impl_->cursor_iter_end.reset();
    1608                 :        127 :   impl_->cursor_msg_view.reset();
    1609                 :            : 
    1610         [ +  + ]:        211 :   for (auto& wrapper_file : impl_->file_list) {
    1611         [ +  - ]:         84 :     if (wrapper_file.reader) {
    1612         [ +  - ]:         84 :       wrapper_file.reader->close();
    1613                 :         84 :       wrapper_file.reader.reset();
    1614                 :            :     }
    1615                 :            :   }
    1616                 :            : 
    1617                 :        127 :   impl_->file_list.clear();
    1618                 :        127 : }
    1619                 :            : 
    1620                 :         14 : int VCAPReader::get_reset_index(const Config& config) {
    1621                 :         14 :   impl_->is_pending.store(true, std::memory_order_relaxed);
    1622                 :            : 
    1623                 :            :   // LCOV_EXCL_START GCOVR_EXCL_START
    1624                 :            :   auto status_function = [](const mcap::Status& status) {
    1625                 :            :     if (!status.ok()) {
    1626                 :            :       CLOG_W("VCAPReader: Failed to read message, error = %s.", status.message.c_str());
    1627                 :            :     }
    1628                 :            :   };
    1629                 :            :   // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1630                 :            : 
    1631                 :         48 :   auto filter_function = [this, &config](std::string_view url) -> bool {
    1632                 :         48 :     return match_playback_url_filter(url, config.filter_urls);
    1633                 :         14 :   };
    1634                 :            : 
    1635                 :         14 :   int start_index = -1;
    1636                 :            : 
    1637                 :         14 :   int64_t last_time = impl_->begin_time.load(std::memory_order_relaxed);
    1638                 :            : 
    1639                 :         14 :   mcap::ReadMessageOptions read_options;
    1640                 :            : 
    1641         [ +  + ]:         28 :   for (auto& wrapper_file : impl_->file_list) {
    1642   [ +  -  +  -  :         42 :     if (start_index < 0 && impl_->begin_time.load(std::memory_order_relaxed) >= last_time &&
                   +  - ]
    1643         [ +  - ]:         28 :         impl_->begin_time.load(std::memory_order_relaxed) <= wrapper_file.end) {
    1644         [ +  + ]:         28 :       if (impl_->begin_time.load(std::memory_order_relaxed) > 0) {
    1645                 :          2 :         read_options.startTime =
    1646                 :          4 :             impl_->begin_time.load(std::memory_order_relaxed) * 1000'000 + impl_->total_start_timestamp_ns;
    1647                 :          2 :         read_options.endTime = mcap::MaxTime;
    1648                 :          2 :         read_options.topicFilter = filter_function;
    1649                 :          2 :         read_options.readOrder = mcap::ReadMessageOptions::ReadOrder::FileOrder;
    1650                 :            :       } else {
    1651                 :         12 :         read_options.startTime = 0;
    1652                 :         12 :         read_options.endTime = mcap::MaxTime;
    1653                 :         12 :         read_options.topicFilter = filter_function;
    1654                 :         12 :         read_options.readOrder = mcap::ReadMessageOptions::ReadOrder::FileOrder;
    1655                 :            :       }
    1656                 :            : 
    1657                 :         14 :       start_index = wrapper_file.index;
    1658                 :            :     } else {
    1659                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
    1660                 :            :       read_options.startTime = 0;
    1661                 :            :       read_options.endTime = mcap::MaxTime;
    1662                 :            :       read_options.topicFilter = filter_function;
    1663                 :            :       read_options.readOrder = mcap::ReadMessageOptions::ReadOrder::FileOrder;
    1664                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1665                 :            :     }
    1666                 :            : 
    1667                 :         14 :     const auto [start_offset, end_offset] =
    1668         [ +  - ]:         14 :         wrapper_file.reader->byteRange(read_options.startTime, read_options.endTime);
    1669                 :            : 
    1670                 :            :     // NOLINTNEXTLINE(readability-redundant-smartptr-get)
    1671         [ +  - ]:         28 :     wrapper_file.msg_view = std::make_unique<mcap::LinearMessageView>(*wrapper_file.reader.get(), read_options,
    1672                 :         14 :                                                                       start_offset, end_offset, status_function);
    1673                 :            : 
    1674         [ -  + ]:         14 :     if (start_offset == end_offset) {
    1675                 :            :       wrapper_file.msg_view_begin.emplace(wrapper_file.msg_view->end());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1676                 :            :       wrapper_file.msg_view_end.emplace(wrapper_file.msg_view->end());    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1677                 :            :     } else {
    1678         [ +  - ]:         14 :       wrapper_file.msg_view_begin.emplace(wrapper_file.msg_view->begin());
    1679         [ +  - ]:         14 :       wrapper_file.msg_view_end.emplace(wrapper_file.msg_view->end());
    1680                 :            :     }
    1681                 :            : 
    1682                 :         14 :     last_time = wrapper_file.end;
    1683                 :            :   }
    1684                 :            : 
    1685                 :         14 :   impl_->is_pending.store(false, std::memory_order_relaxed);
    1686                 :            : 
    1687                 :         14 :   return start_index;
    1688                 :         14 : }
    1689                 :            : 
    1690                 :         51 : bool VCAPReader::prepare_cursor_view(int file_index) {
    1691                 :         51 :   impl_->cursor_iter.reset();
    1692                 :         51 :   impl_->cursor_iter_end.reset();
    1693                 :         51 :   impl_->cursor_msg_view.reset();
    1694                 :         51 :   impl_->cursor_need_advance = false;
    1695                 :            : 
    1696   [ +  -  -  +  :         51 :   if VUNLIKELY (file_index < 0 || file_index >= static_cast<int>(impl_->file_list.size())) {
                   -  + ]
    1697                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1698                 :            :   }
    1699                 :            : 
    1700         [ +  - ]:         51 :   auto& wrapper_file = impl_->file_list.at(file_index);
    1701                 :            : 
    1702         [ -  + ]:         51 :   if VUNLIKELY (!wrapper_file.reader) {
    1703                 :            :     VLOG_W("VCAPReader: Cursor target vcap reader is empty.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1704                 :            :     return false;                                               // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1705                 :            :   }
    1706                 :            : 
    1707                 :          0 :   auto status_function = [this](const mcap::Status& status) {
    1708                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
    1709                 :            :     if (!status.ok()) {
    1710                 :            :       CLOG_W("VCAPReader: Failed to read cursor message, error = %s.", status.message.c_str());
    1711                 :            :       impl_->cursor_read_error = true;
    1712                 :            :     }
    1713                 :            :   };
    1714                 :            :   // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1715                 :            : 
    1716                 :         72 :   auto filter_function = [this](std::string_view url) -> bool {
    1717                 :         72 :     return match_playback_url_filter(url, impl_->cursor_config.filter_urls);
    1718                 :         51 :   };
    1719                 :            : 
    1720                 :         51 :   mcap::ReadMessageOptions read_options;
    1721                 :         51 :   read_options.startTime =
    1722         [ +  + ]:         51 :       impl_->cursor_begin_us > 0 ? impl_->cursor_begin_us * 1000 + impl_->total_start_timestamp_ns : 0;
    1723                 :         51 :   read_options.endTime = mcap::MaxTime;
    1724                 :         51 :   read_options.topicFilter = filter_function;
    1725                 :         51 :   read_options.readOrder = mcap::ReadMessageOptions::ReadOrder::FileOrder;
    1726                 :            : 
    1727         [ +  - ]:         51 :   const auto [start_offset, end_offset] = wrapper_file.reader->byteRange(read_options.startTime, read_options.endTime);
    1728                 :            : 
    1729                 :            :   // NOLINTNEXTLINE(readability-redundant-smartptr-get)
    1730         [ +  - ]:        102 :   impl_->cursor_msg_view = std::make_unique<mcap::LinearMessageView>(*wrapper_file.reader.get(), read_options,
    1731                 :         51 :                                                                      start_offset, end_offset, status_function);
    1732                 :            : 
    1733         [ +  + ]:         51 :   if (start_offset == end_offset) {
    1734         [ +  - ]:          1 :     impl_->cursor_iter.emplace(impl_->cursor_msg_view->end());
    1735                 :            :   } else {
    1736         [ +  - ]:         50 :     impl_->cursor_iter.emplace(impl_->cursor_msg_view->begin());
    1737                 :            :   }
    1738                 :            : 
    1739         [ +  - ]:         51 :   impl_->cursor_iter_end.emplace(impl_->cursor_msg_view->end());
    1740                 :         51 :   impl_->cursor_file_index = file_index;
    1741                 :            : 
    1742                 :         51 :   return true;
    1743                 :         51 : }
    1744                 :            : 
    1745                 :         41 : bool VCAPReader::do_open_cursor(const Config& config) {
    1746                 :         41 :   impl_->cursor_config = config;
    1747         [ +  + ]:         41 :   impl_->cursor_begin_us = config.begin_time > 0 ? config.begin_time * 1000 : 0;
    1748         [ +  + ]:         41 :   impl_->cursor_end_us = config.end_time > 0 ? config.end_time * 1000 : 0;
    1749                 :         41 :   impl_->cursor_file_index = 0;
    1750                 :         41 :   impl_->cursor_read_error = false;
    1751                 :            : 
    1752         [ -  + ]:         41 :   if VUNLIKELY (impl_->file_list.empty()) {
    1753                 :            :     VLOG_W("VCAPReader: Cursor cannot find any data.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1754                 :            :     return false;                                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1755                 :            :   }
    1756                 :            : 
    1757                 :         41 :   return prepare_cursor_view(0);
    1758                 :            : }
    1759                 :            : 
    1760                 :         95 : bool VCAPReader::do_read_next(Frame& out, bool& is_error) {
    1761                 :         95 :   is_error = false;
    1762                 :            : 
    1763                 :            :   while (true) {
    1764   [ +  -  -  +  :        105 :     if VUNLIKELY (!impl_->cursor_iter.has_value() || !impl_->cursor_iter_end.has_value()) {
                   -  + ]
    1765                 :         95 :       return false;
    1766                 :            :     }
    1767                 :            : 
    1768         [ +  - ]:        105 :     auto& iter = impl_->cursor_iter.value();
    1769         [ +  - ]:        105 :     auto& iter_end = impl_->cursor_iter_end.value();
    1770                 :            : 
    1771         [ +  + ]:        105 :     if (impl_->cursor_need_advance) {
    1772   [ +  -  +  - ]:         54 :       if (iter != iter_end) {
    1773         [ +  - ]:         54 :         iter++;
    1774                 :            :       }
    1775                 :            : 
    1776                 :         54 :       impl_->cursor_need_advance = false;
    1777                 :            :     }
    1778                 :            : 
    1779   [ +  -  +  + ]:        105 :     if (iter == iter_end) {
    1780         [ -  + ]:         47 :       if VUNLIKELY (impl_->cursor_read_error) {
    1781                 :            :         is_error = true;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1782                 :            :         return false;     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1783                 :            :       }
    1784                 :            : 
    1785         [ +  + ]:         47 :       if (impl_->cursor_file_index + 1 < static_cast<int>(impl_->file_list.size())) {
    1786   [ +  -  -  + ]:         10 :         if VUNLIKELY (!prepare_cursor_view(impl_->cursor_file_index + 1)) {
    1787                 :            :           is_error = true;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1788                 :            :           return false;     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1789                 :            :         }
    1790                 :            : 
    1791                 :         10 :         continue;
    1792                 :            :       }
    1793                 :            : 
    1794                 :         37 :       return false;
    1795                 :            :     }
    1796                 :            : 
    1797         [ +  - ]:         58 :     const int64_t timestamp = (iter->message.logTime - impl_->total_start_timestamp_ns) / 1000;
    1798                 :            : 
    1799   [ +  +  -  +  :         58 :     if (impl_->cursor_begin_us > 0 && timestamp < impl_->cursor_begin_us) {
                   -  + ]
    1800                 :            :       iter++;    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1801                 :            :       continue;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1802                 :            :     }
    1803                 :            : 
    1804   [ +  +  +  +  :         58 :     if (impl_->cursor_end_us > 0 && timestamp > impl_->cursor_end_us) {
                   +  + ]
    1805                 :          4 :       return false;
    1806                 :            :     }
    1807                 :            : 
    1808   [ +  -  -  + ]:         54 :     if VUNLIKELY (iter->message.dataSize > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) {
    1809                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
    1810                 :            :       CLOG_W("VCAPReader: Cursor message data size is too large to address, size = %" PRIu64 ".",
    1811                 :            :              static_cast<uint64_t>(iter->message.dataSize));
    1812                 :            :       iter++;
    1813                 :            :       continue;
    1814                 :            :     }
    1815                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1816                 :            : 
    1817                 :         54 :     std::string output_url;
    1818                 :            : 
    1819   [ +  -  +  -  :         54 :     if VUNLIKELY (!convert_playback_url(iter->channel->topic, output_url)) {
                   -  + ]
    1820                 :            :       iter++;    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1821                 :            :       continue;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1822                 :            :     }
    1823                 :            : 
    1824                 :         54 :     ActionType action_type = ActionType::kUnknownAction;
    1825         [ +  - ]:         54 :     auto& wrapper_file = impl_->file_list.at(impl_->cursor_file_index);
    1826                 :            : 
    1827   [ +  -  +  - ]:         54 :     if (auto action_iter = wrapper_file.channel_action_map.find(iter->message.channelId);
    1828         [ +  - ]:         54 :         action_iter != wrapper_file.channel_action_map.end()) {
    1829                 :         54 :       action_type = action_iter->second;
    1830                 :            :     }
    1831                 :            : 
    1832         [ +  - ]:         54 :     const auto* data = reinterpret_cast<const uint8_t*>(iter->message.data);
    1833         [ +  - ]:         54 :     const auto size = static_cast<size_t>(iter->message.dataSize);
    1834                 :            : 
    1835                 :         54 :     out.timestamp = timestamp;
    1836                 :         54 :     out.url = std::move(output_url);
    1837                 :         54 :     out.ser_type.clear();
    1838                 :         54 :     out.schema_type = SchemaType::kUnknown;
    1839                 :         54 :     out.action_type = action_type;
    1840                 :         54 :     out.data = Bytes::shallow_copy(data, size);
    1841                 :            : 
    1842         [ +  - ]:         54 :     fill_frame_meta(out);
    1843                 :            : 
    1844                 :         54 :     impl_->cursor_need_advance = true;
    1845                 :            : 
    1846                 :         54 :     return true;
    1847         [ +  - ]:         64 :   }
    1848                 :            : }
    1849                 :            : 
    1850                 :         13 : void VCAPReader::read(const Config& config) {
    1851                 :         13 :   int loop_times = 0;
    1852                 :            : 
    1853         [ +  + ]:         13 :   if (config.auto_pause) {
    1854                 :          1 :     impl_->pause_flag.store(true, std::memory_order_relaxed);
    1855                 :            :   }
    1856                 :            : 
    1857                 :         13 :   bool is_interrupted = false;
    1858                 :            : 
    1859                 :            :   do {
    1860                 :         14 :     bool is_end = false;
    1861                 :            : 
    1862                 :            :     // prepare
    1863         [ +  - ]:         14 :     int start_index = get_reset_index(config);
    1864                 :            : 
    1865         [ +  + ]:         14 :     if (impl_->ready_callback) {
    1866         [ +  - ]:          8 :       impl_->ready_callback();
    1867                 :            :     }
    1868                 :            : 
    1869   [ +  -  -  +  :         14 :     if VUNLIKELY (start_index < 0 || start_index > static_cast<int>(impl_->file_list.size()) - 1) {
                   -  + ]
    1870                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
    1871                 :            :       VLOG_W("VCAPReader: Cannot find any data for play.");
    1872                 :            : 
    1873                 :            :       update_status(kStopped);
    1874                 :            : 
    1875                 :            :       if (config.auto_quit) {
    1876                 :            :         quit();
    1877                 :            :       }
    1878                 :            : 
    1879                 :            :       return;
    1880                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1881                 :            :     }
    1882                 :            : 
    1883                 :            :     {
    1884         [ +  - ]:         14 :       std::lock_guard time_lock(impl_->time_mtx);
    1885                 :         14 :       impl_->pause_elapsed.store(0, std::memory_order_relaxed);
    1886                 :         14 :       impl_->offset_elapsed.store(0, std::memory_order_relaxed);
    1887                 :         28 :       impl_->real_elapsed.store(impl_->begin_time.load(std::memory_order_relaxed) * 1000U, std::memory_order_relaxed);
    1888                 :            : 
    1889                 :         14 :       impl_->elapsed_timer.restart();
    1890                 :         14 :       impl_->pause_elapsed_timer.restart();
    1891                 :         14 :       impl_->offset_timer.restart();
    1892                 :         14 :       impl_->real_timer.restart();
    1893                 :         14 :     }
    1894                 :            : 
    1895         [ +  + ]:         14 :     if (impl_->stop_flag.load(std::memory_order_relaxed)) {
    1896                 :          1 :       is_interrupted = true;
    1897         [ +  - ]:          1 :       update_status(kStopped);
    1898                 :          5 :       break;
    1899         [ +  + ]:         13 :     } else if (impl_->jump_flag.load(std::memory_order_relaxed)) {
    1900                 :          2 :       break;
    1901         [ +  + ]:         11 :     } else if (impl_->pause_flag.load(std::memory_order_relaxed)) {
    1902                 :          1 :       impl_->pause_elapsed_timer.restart();
    1903         [ +  - ]:          1 :       update_status(kPaused);
    1904         [ +  - ]:          1 :       do_pause();
    1905                 :            :       // impl_->pause_next_flag = false;
    1906                 :            :       {
    1907         [ +  - ]:          1 :         std::lock_guard time_lock(impl_->time_mtx);
    1908                 :          1 :         impl_->pause_elapsed.store(0, std::memory_order_relaxed);
    1909                 :          1 :         impl_->offset_elapsed.store(0, std::memory_order_relaxed);
    1910                 :          2 :         impl_->real_elapsed.store(impl_->begin_time.load(std::memory_order_relaxed) * 1000U, std::memory_order_relaxed);
    1911                 :            : 
    1912                 :          1 :         impl_->elapsed_timer.restart();
    1913                 :          1 :         impl_->pause_elapsed_timer.restart();
    1914                 :          1 :         impl_->offset_timer.restart();
    1915                 :          1 :         impl_->real_timer.restart();
    1916                 :          1 :       }
    1917                 :            : 
    1918                 :            :     } else {
    1919         [ +  - ]:         10 :       update_status(kPlaying);
    1920                 :            :     }
    1921                 :            : 
    1922                 :         11 :     int64_t elapsed = 0;
    1923                 :         11 :     int64_t timestamp = 0;
    1924                 :         11 :     int64_t last_timestamp = 0;
    1925                 :         11 :     const uint8_t* data = nullptr;
    1926                 :         11 :     size_t size = 0;
    1927                 :            : 
    1928                 :            :     // process files
    1929         [ +  + ]:         20 :     for (int index = start_index; index < static_cast<int>(impl_->file_list.size()); ++index) {
    1930                 :         11 :       impl_->split_index.store(index, std::memory_order_relaxed);
    1931                 :            : 
    1932         [ +  - ]:         22 :       auto& wrapper_file = impl_->file_list.at(impl_->split_index.load(std::memory_order_relaxed));
    1933                 :            : 
    1934         [ -  + ]:         11 :       if VUNLIKELY (!wrapper_file.reader) {
    1935                 :            :         VLOG_W("VCAPReader: Target vcap reader is empty.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1936                 :            :         return;                                              // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1937                 :            :       }
    1938                 :            : 
    1939                 :            :       // process datas
    1940         [ +  - ]:         11 :       auto iter = std::move(wrapper_file.msg_view_begin).value();    // NOLINT
    1941         [ +  - ]:         11 :       auto iter_end = std::move(wrapper_file.msg_view_end).value();  // NOLINT
    1942                 :            : 
    1943   [ +  -  +  -  :         46 :       for (; iter != iter_end; iter++) {
                   +  + ]
    1944         [ +  - ]:         37 :         timestamp = (iter->message.logTime - impl_->total_start_timestamp_ns) / 1000;
    1945                 :            : 
    1946         [ -  + ]:         37 :         if VUNLIKELY (last_timestamp > timestamp + 10'000U) {
    1947                 :            :           VLOG_W("VCAPReader: The vcap timestamp is incorrect.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1948                 :            :         }
    1949                 :            : 
    1950                 :         37 :         last_timestamp = timestamp;
    1951                 :            : 
    1952         [ -  + ]:         74 :         if (timestamp < impl_->begin_time.load(std::memory_order_relaxed) * 1000U) {
    1953                 :            :           continue;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1954                 :            :         }
    1955                 :            : 
    1956   [ +  +  -  + ]:         37 :         if (config.end_time > 0 && timestamp > config.end_time * 1000U) {
    1957                 :            :           timestamp = config.end_time * 1000U;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1958                 :            :           is_end = true;                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1959                 :            :         }
    1960                 :            : 
    1961         [ +  - ]:         37 :         data = reinterpret_cast<const uint8_t*>(iter->message.data);
    1962                 :            : 
    1963   [ +  -  -  + ]:         37 :         if VUNLIKELY (iter->message.dataSize > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) {
    1964                 :            :           // LCOV_EXCL_START GCOVR_EXCL_START
    1965                 :            :           CLOG_W("VCAPReader: Message data size is too large to address, size = %" PRIu64 ".",
    1966                 :            :                  static_cast<uint64_t>(iter->message.dataSize));
    1967                 :            :           continue;
    1968                 :            :         }
    1969                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1970                 :            : 
    1971         [ +  - ]:         37 :         size = static_cast<size_t>(iter->message.dataSize);
    1972                 :            : 
    1973                 :         37 :         elapsed =
    1974                 :         37 :             (timestamp / impl_->rate.load(std::memory_order_relaxed)) -
    1975                 :         37 :             (impl_->elapsed_timer.get() - impl_->pause_elapsed.load(std::memory_order_relaxed)) -
    1976                 :         74 :             (impl_->begin_time.load(std::memory_order_relaxed) * 1000U / impl_->rate.load(std::memory_order_relaxed));
    1977                 :            : 
    1978                 :            :         {
    1979         [ +  - ]:         37 :           std::unique_lock lock(impl_->mtx);
    1980                 :            : 
    1981         [ +  + ]:         37 :           if (config.force_delay > 0) {
    1982                 :         25 :             impl_->cv.wait_for(lock, std::chrono::milliseconds(config.force_delay), [this]() -> bool {
    1983         [ +  - ]:        101 :               return impl_->stop_flag.load(std::memory_order_relaxed) ||
    1984         [ +  + ]:        100 :                      impl_->pause_next_flag.load(std::memory_order_relaxed) ||
    1985   [ +  +  -  + ]:        151 :                      impl_->jump_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
    1986                 :            :             });
    1987   [ +  +  +  - ]:         12 :           } else if (config.force_delay < 0 && elapsed > 0) {
    1988                 :          1 :             impl_->offset_timer.restart();
    1989                 :            : 
    1990                 :          1 :             impl_->cv.wait_for(lock, std::chrono::microseconds(elapsed), [this]() -> bool {
    1991         [ +  - ]:          4 :               return impl_->stop_flag.load(std::memory_order_relaxed) ||
    1992         [ +  - ]:          4 :                      impl_->pause_next_flag.load(std::memory_order_relaxed) ||
    1993         [ +  - ]:          4 :                      impl_->jump_flag.load(std::memory_order_relaxed) ||
    1994   [ +  -  -  + ]:          6 :                      impl_->pause_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
    1995                 :            :             });
    1996                 :            : 
    1997         [ -  + ]:          1 :             if VUNLIKELY (impl_->pause_flag.load(std::memory_order_relaxed)) {
    1998                 :            :               impl_->offset_elapsed.store(elapsed - impl_->offset_timer.get(),  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1999                 :            :                                           std::memory_order_relaxed);           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2000                 :            :             }
    2001                 :            :           }
    2002                 :         37 :         }
    2003                 :            : 
    2004   [ +  +  +  +  :         72 :         if (impl_->stop_flag.load(std::memory_order_relaxed) || impl_->jump_flag.load(std::memory_order_relaxed) ||
                   +  + ]
    2005   [ +  -  -  + ]:         35 :             is_ready_to_quit()) {
    2006                 :          2 :           is_interrupted = true;
    2007                 :          2 :           break;
    2008         [ +  + ]:         35 :         } else if (impl_->pause_flag.load(std::memory_order_relaxed)) {
    2009         [ +  - ]:          3 :           do_pause();
    2010                 :          3 :           impl_->pause_next_flag.store(false, std::memory_order_relaxed);
    2011                 :            : 
    2012   [ +  -  +  -  :          6 :           if (impl_->stop_flag.load(std::memory_order_relaxed) || impl_->jump_flag.load(std::memory_order_relaxed) ||
                   -  + ]
    2013   [ +  -  -  + ]:          3 :               is_ready_to_quit()) {
    2014                 :            :             is_interrupted = true;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2015                 :            :             break;                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2016                 :            :           }
    2017                 :            :         }
    2018                 :            : 
    2019                 :            :         {
    2020         [ +  - ]:         35 :           std::lock_guard time_lock(impl_->time_mtx);
    2021                 :         35 :           impl_->real_timer.restart();
    2022                 :         35 :           impl_->real_elapsed.store(timestamp, std::memory_order_relaxed);
    2023                 :         35 :         }
    2024                 :            : 
    2025         [ -  + ]:         35 :         if (is_end) {
    2026                 :            :           break;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    2027                 :            :         }
    2028                 :            : 
    2029                 :         35 :         ActionType action_type = ActionType::kUnknownAction;
    2030                 :            : 
    2031   [ +  -  +  - ]:         35 :         if (auto action_iter = wrapper_file.channel_action_map.find(iter->message.channelId);
    2032         [ +  - ]:         35 :             action_iter != wrapper_file.channel_action_map.end()) {
    2033                 :         35 :           action_type = action_iter->second;
    2034                 :            :         }
    2035                 :            : 
    2036                 :         35 :         Frame frame;
    2037                 :         35 :         frame.timestamp = timestamp;
    2038   [ +  -  +  - ]:         35 :         frame.url = iter->channel->topic;
    2039                 :         35 :         frame.action_type = action_type;
    2040                 :         35 :         frame.data = Bytes::shallow_copy(data, size);
    2041                 :            : 
    2042         [ +  - ]:         35 :         BagReader::process_output(frame);
    2043                 :         35 :       }
    2044                 :            : 
    2045   [ +  +  -  + ]:         11 :       if (is_interrupted || is_end) {
    2046                 :            :         break;
    2047                 :            :       }
    2048   [ +  +  +  + ]:         13 :     }
    2049                 :            : 
    2050         [ +  + ]:         11 :     if (is_interrupted) {
    2051         [ +  + ]:          2 :       if (impl_->stop_flag.load(std::memory_order_relaxed)) {
    2052         [ +  - ]:          1 :         update_status(kStopped);
    2053                 :            :       }
    2054                 :            : 
    2055                 :          2 :       break;
    2056                 :            :     }
    2057                 :            : 
    2058         [ +  - ]:          9 :     if (!impl_->jump_flag.load(std::memory_order_relaxed)) {
    2059         [ +  - ]:          9 :       update_status(kStopped);
    2060                 :            : 
    2061         [ +  + ]:          9 :       if (config.skip_blank) {
    2062                 :          1 :         impl_->begin_time.store(std::max(config.begin_time, impl_->info.blank_duration), std::memory_order_relaxed);
    2063                 :            :       } else {
    2064                 :          8 :         impl_->begin_time.store(config.begin_time, std::memory_order_relaxed);
    2065                 :            :       }
    2066                 :            :     }
    2067   [ +  -  +  + ]:         27 :   } while (impl_->times.load(std::memory_order_relaxed) <= 0 ||
    2068         [ +  - ]:         18 :            (impl_->times.load(std::memory_order_relaxed) > 0 &&
    2069         [ +  + ]:         18 :             ++loop_times < impl_->times.load(std::memory_order_relaxed)));
    2070                 :            : 
    2071         [ +  + ]:         13 :   if (impl_->stop_flag.load(std::memory_order_relaxed)) {
    2072                 :          2 :     is_interrupted = true;
    2073                 :            :   }
    2074                 :            : 
    2075   [ +  +  +  +  :         13 :   if (!impl_->jump_flag.load(std::memory_order_relaxed) && !is_interrupted) {
                   +  + ]
    2076                 :          8 :     flush_plugin();
    2077                 :            :   }
    2078                 :            : 
    2079   [ +  +  +  +  :         13 :   if (!impl_->jump_flag.load(std::memory_order_relaxed) && impl_->finish_callback) {
                   +  + ]
    2080                 :          9 :     impl_->finish_callback(is_interrupted);
    2081                 :            :   }
    2082                 :            : 
    2083   [ +  +  +  +  :         13 :   if (!impl_->jump_flag.load(std::memory_order_relaxed) && config.auto_quit) {
                   +  + ]
    2084                 :          1 :     quit();
    2085                 :            :   }
    2086                 :            : 
    2087                 :            :   // clean msg_view
    2088         [ +  + ]:         26 :   for (auto& wrapper_file : impl_->file_list) {
    2089                 :         13 :     wrapper_file.msg_view.reset();
    2090                 :            :   }
    2091                 :            : }
    2092                 :            : 
    2093                 :            : }  // namespace vlink

Generated by: LCOV version 1.14