diff --git a/src/clp_ffi_js/ir/StreamReader.cpp b/src/clp_ffi_js/ir/StreamReader.cpp index be8c011b..0359000f 100644 --- a/src/clp_ffi_js/ir/StreamReader.cpp +++ b/src/clp_ffi_js/ir/StreamReader.cpp @@ -129,6 +129,7 @@ EMSCRIPTEN_BINDINGS(ClpStreamReader) { "Array<[string, bigint, number, number]>" ); emscripten::register_type("number[] | null"); + emscripten::register_type("number | null"); emscripten::class_("ClpStreamReader") .constructor( &clp_ffi_js::ir::StreamReader::create, @@ -145,7 +146,11 @@ EMSCRIPTEN_BINDINGS(ClpStreamReader) { ) .function("filterLogEvents", &clp_ffi_js::ir::StreamReader::filter_log_events) .function("deserializeStream", &clp_ffi_js::ir::StreamReader::deserialize_stream) - .function("decodeRange", &clp_ffi_js::ir::StreamReader::decode_range); + .function("decodeRange", &clp_ffi_js::ir::StreamReader::decode_range) + .function( + "findNearestLogEventByTimestamp", + &clp_ffi_js::ir::StreamReader::find_nearest_log_event_by_timestamp + ); } } // namespace diff --git a/src/clp_ffi_js/ir/StreamReader.hpp b/src/clp_ffi_js/ir/StreamReader.hpp index 6d483e89..25ddcc57 100644 --- a/src/clp_ffi_js/ir/StreamReader.hpp +++ b/src/clp_ffi_js/ir/StreamReader.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,7 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(ReaderOptions); // JS types used as outputs EMSCRIPTEN_DECLARE_VAL_TYPE(DecodedResultsTsType); EMSCRIPTEN_DECLARE_VAL_TYPE(FilteredLogEventMapTsType); +EMSCRIPTEN_DECLARE_VAL_TYPE(NullableLogEventIdx); enum class StreamType : uint8_t { Structured, @@ -124,6 +126,24 @@ class StreamReader { [[nodiscard]] virtual auto decode_range(size_t begin_idx, size_t end_idx, bool use_filter) const -> DecodedResultsTsType = 0; + /** + * Finds the log event, L, where if we assume: + * + * - the collection of log events is sorted in chronological order, or the search won't work; + * - and we insert a marker log event, M, with timestamp `target_ts` into the collection (if log + * events with timestamp `target_ts` already exist in the collection, M should be inserted + * after them). + * + * L is the event just before M, if M is not the first event in the collection; otherwise L is + * the event just after M. + * + * @param target_ts + * @return The index of the log event L. + */ + [[nodiscard]] virtual auto find_nearest_log_event_by_timestamp( + clp::ir::epoch_time_ms_t target_ts + ) -> NullableLogEventIdx = 0; + protected: explicit StreamReader() = default; @@ -172,6 +192,20 @@ class StreamReader { LogLevelFilterTsType const& log_level_filter, LogEvents const& log_events ) -> void; + + /** + * Templated implementation of `find_nearest_log_event_by_timestamp`. + * + * @tparam LogEvent + * @param log_events + * @param target_ts + * @return See `find_nearest_log_event_by_timestamp`. + */ + template + auto generic_find_nearest_log_event_by_timestamp( + LogEvents const& log_events, + clp::ir::epoch_time_ms_t target_ts + ) -> NullableLogEventIdx; }; template @@ -258,6 +292,34 @@ auto StreamReader::generic_filter_log_events( } } } + +template +auto StreamReader::generic_find_nearest_log_event_by_timestamp( + LogEvents const& log_events, + clp::ir::epoch_time_ms_t target_ts +) -> NullableLogEventIdx { + if (log_events.empty()) { + return NullableLogEventIdx{emscripten::val::null()}; + } + + // Find the log event whose timestamp is just after `target_ts` + auto first_greater_it{std::upper_bound( + log_events.begin(), + log_events.end(), + target_ts, + [](clp::ir::epoch_time_ms_t ts, LogEventWithFilterData const& log_event) { + return ts < log_event.get_timestamp(); + } + )}; + + if (first_greater_it == log_events.begin()) { + return NullableLogEventIdx{emscripten::val(0)}; + } + + auto const first_greater_idx{std::distance(log_events.begin(), first_greater_it)}; + + return NullableLogEventIdx{emscripten::val(first_greater_idx - 1)}; +} } // namespace clp_ffi_js::ir #endif // CLP_FFI_JS_IR_STREAMREADER_HPP diff --git a/src/clp_ffi_js/ir/StructuredIrStreamReader.cpp b/src/clp_ffi_js/ir/StructuredIrStreamReader.cpp index f12b66b4..47a47732 100644 --- a/src/clp_ffi_js/ir/StructuredIrStreamReader.cpp +++ b/src/clp_ffi_js/ir/StructuredIrStreamReader.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,12 @@ auto StructuredIrStreamReader::decode_range(size_t begin_idx, size_t end_idx, bo ); } +auto StructuredIrStreamReader::find_nearest_log_event_by_timestamp( + clp::ir::epoch_time_ms_t const target_ts +) -> NullableLogEventIdx { + return generic_find_nearest_log_event_by_timestamp(*m_deserialized_log_events, target_ts); +} + StructuredIrStreamReader::StructuredIrStreamReader( StreamReaderDataContext&& stream_reader_data_context, std::shared_ptr deserialized_log_events diff --git a/src/clp_ffi_js/ir/StructuredIrStreamReader.hpp b/src/clp_ffi_js/ir/StructuredIrStreamReader.hpp index 6ccd5255..0d48aee0 100644 --- a/src/clp_ffi_js/ir/StructuredIrStreamReader.hpp +++ b/src/clp_ffi_js/ir/StructuredIrStreamReader.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,9 @@ class StructuredIrStreamReader : public StreamReader { [[nodiscard]] auto decode_range(size_t begin_idx, size_t end_idx, bool use_filter) const -> DecodedResultsTsType override; + [[nodiscard]] auto find_nearest_log_event_by_timestamp(clp::ir::epoch_time_ms_t target_ts + ) -> NullableLogEventIdx override; + private: // Constructor explicit StructuredIrStreamReader( diff --git a/src/clp_ffi_js/ir/UnstructuredIrStreamReader.cpp b/src/clp_ffi_js/ir/UnstructuredIrStreamReader.cpp index 22085e02..7b4c1cf3 100644 --- a/src/clp_ffi_js/ir/UnstructuredIrStreamReader.cpp +++ b/src/clp_ffi_js/ir/UnstructuredIrStreamReader.cpp @@ -158,6 +158,12 @@ auto UnstructuredIrStreamReader::decode_range(size_t begin_idx, size_t end_idx, ); } +auto UnstructuredIrStreamReader::find_nearest_log_event_by_timestamp( + clp::ir::epoch_time_ms_t const target_ts +) -> NullableLogEventIdx { + return generic_find_nearest_log_event_by_timestamp(m_encoded_log_events, target_ts); +} + UnstructuredIrStreamReader::UnstructuredIrStreamReader( StreamReaderDataContext&& stream_reader_data_context ) diff --git a/src/clp_ffi_js/ir/UnstructuredIrStreamReader.hpp b/src/clp_ffi_js/ir/UnstructuredIrStreamReader.hpp index 60e515f3..f9ece434 100644 --- a/src/clp_ffi_js/ir/UnstructuredIrStreamReader.hpp +++ b/src/clp_ffi_js/ir/UnstructuredIrStreamReader.hpp @@ -71,6 +71,9 @@ class UnstructuredIrStreamReader : public StreamReader { [[nodiscard]] auto decode_range(size_t begin_idx, size_t end_idx, bool use_filter) const -> DecodedResultsTsType override; + [[nodiscard]] auto find_nearest_log_event_by_timestamp(clp::ir::epoch_time_ms_t target_ts + ) -> NullableLogEventIdx override; + private: // Constructor explicit UnstructuredIrStreamReader(