From 8a4b4e243c7573f3026f3c3259e73cd8963c9283 Mon Sep 17 00:00:00 2001 From: Alexander Sopov Date: Fri, 27 Feb 2026 11:57:35 +0100 Subject: [PATCH 1/6] Refactor PartitionsSaxHandler to use boost::json Previously it was based on rapidjson The tests are adjusted accordingly. Relates-To: OCMAM-448 Signed-off-by: Alexander Sopov --- olp-cpp-sdk-dataservice-read/CMakeLists.txt | 8 + .../src/repositories/AsyncJsonStream.cpp | 22 ++- .../src/repositories/AsyncJsonStream.h | 12 +- .../src/repositories/PartitionsRepository.cpp | 44 +++-- .../src/repositories/PartitionsSaxHandler.cpp | 73 ++++---- .../src/repositories/PartitionsSaxHandler.h | 95 ++++++++-- .../tests/AsyncJsonStreamTest.cpp | 14 +- .../tests/PartitionsSaxHandlerTest.cpp | 177 ++++++++++-------- 8 files changed, 283 insertions(+), 162 deletions(-) diff --git a/olp-cpp-sdk-dataservice-read/CMakeLists.txt b/olp-cpp-sdk-dataservice-read/CMakeLists.txt index b0e8c05c4..b78da3102 100644 --- a/olp-cpp-sdk-dataservice-read/CMakeLists.txt +++ b/olp-cpp-sdk-dataservice-read/CMakeLists.txt @@ -21,13 +21,21 @@ set(DESCRIPTION "C++ API library for reading OLP data") file(GLOB_RECURSE INC "include/*.h*") file(GLOB_RECURSE SRC "src/*.*") +find_package(Boost REQUIRED) + add_library(${PROJECT_NAME} ${SRC} ${INC}) +target_compile_definitions(${PROJECT_NAME} + PRIVATE + BOOST_ALL_NO_LIB + BOOST_JSON_NO_LIB) + target_include_directories(${PROJECT_NAME} PUBLIC $ + $ $ PRIVATE $ PRIVATE ${olp-cpp-sdk-core_INCLUDE_DIRS}) diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp b/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp index a2e0f05e7..bd5549f1b 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp +++ b/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 HERE Europe B.V. + * Copyright (C) 2023-2026 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,20 @@ RapidJsonByteStream::Ch RapidJsonByteStream::Peek() { return read_buffer_[count_]; } +boost::json::string_view RapidJsonByteStream::ReadView() { + if (ReadEmpty()) { + SwapBuffers(); + } + auto terminator_it = + std::find(read_buffer_.begin() + count_, read_buffer_.end(), '\0'); + auto begin = read_buffer_.begin() + count_; + boost::core::basic_string_view::size_type size = + std::distance(begin, terminator_it); + count_ += size; + full_count_ += size; + return {&*begin, size}; +} + RapidJsonByteStream::Ch RapidJsonByteStream::Take() { if (ReadEmpty()) { SwapBuffers(); @@ -41,12 +55,6 @@ RapidJsonByteStream::Ch RapidJsonByteStream::Take() { size_t RapidJsonByteStream::Tell() const { return full_count_; } -// Not implemented -char* RapidJsonByteStream::PutBegin() { return 0; } -void RapidJsonByteStream::Put(char) {} -void RapidJsonByteStream::Flush() {} -size_t RapidJsonByteStream::PutEnd(char*) { return 0; } - bool RapidJsonByteStream::ReadEmpty() const { return count_ == read_buffer_.size(); } diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.h b/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.h index ae1f57ba6..b262d1b19 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.h +++ b/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 HERE Europe B.V. + * Copyright (C) 2023-2026 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #include #include +#include namespace olp { namespace dataservice { @@ -43,15 +44,12 @@ class RapidJsonByteStream { /// character. Ch Take(); + /// Return the view of current read buffer until the end of first \0 character + boost::json::string_view ReadView(); + /// Get the current read cursor. size_t Tell() const; - /// Not needed for reading. - char* PutBegin(); - void Put(char); - void Flush(); - size_t PutEnd(char*); - bool ReadEmpty() const; bool WriteEmpty() const; diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp index d80c9f090..895610f28 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp +++ b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 HERE Europe B.V. + * Copyright (C) 2019-2026 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "CatalogRepository.h" #include "generated/api/MetadataApi.h" #include "generated/api/QueryApi.h" @@ -648,28 +650,36 @@ client::ApiNoResponse PartitionsRepository::ParsePartitionsStream( const std::shared_ptr& async_stream, const PartitionsStreamCallback& partition_callback, client::CancellationContext context) { - rapidjson::ParseResult parse_result; + auto parse_result = + boost::json::make_error_code(boost::json::error::incomplete); // We must perform at least one attempt to parse. do { - rapidjson::Reader reader; - auto partitions_handler = - std::make_shared(partition_callback); - - auto reader_cancellation_token = client::CancellationToken([=]() { - partitions_handler->Abort(); - async_stream->CloseStream(client::ApiError::Cancelled()); - }); - - if (!context.ExecuteOrCancelled( - [=]() { return reader_cancellation_token; })) { + auto parser = + std::make_shared>( + boost::json::parse_options{}, partition_callback); + + auto reader_cancellation_token = + client::CancellationToken([parser, &async_stream]() { + // partitions_handler->Abort(); + parser->handler().Abort(); + async_stream->CloseStream(client::ApiError::Cancelled()); + }); + + if (!context.ExecuteOrCancelled([reader_cancellation_token]() { + return reader_cancellation_token; + })) { return client::ApiError::Cancelled(); } auto json_stream = async_stream->GetCurrentStream(); - parse_result = reader.Parse( - *json_stream, *partitions_handler); + while (json_stream->Peek() != '\0') { + auto view = json_stream->ReadView(); + if (parser->write_some(true, view.data(), view.size(), parse_result)) { + parse_result = {}; + } + } // Retry to parse the stream until it's closed. } while (!async_stream->IsClosed()); @@ -677,8 +687,8 @@ client::ApiNoResponse PartitionsRepository::ParsePartitionsStream( if (error) { return {*error}; - } else if (!parse_result) { - return client::ApiError(parse_result.Code(), "Parsing error"); + } else if (parse_result.failed()) { + return client::ApiError(parse_result.value(), "Parsing error"); } else { return client::ApiNoResult{}; } diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.cpp b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.cpp index 146c34e6c..bc67df12c 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.cpp +++ b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 HERE Europe B.V. + * Copyright (C) 2023-2026 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,46 +35,45 @@ constexpr unsigned long long int HashStringToInt( PartitionsSaxHandler::PartitionsSaxHandler(PartitionCallback partition_callback) : partition_callback_(std::move(partition_callback)) {} -bool PartitionsSaxHandler::StartObject() { +bool PartitionsSaxHandler::on_object_begin(boost::json::error_code& ec) { if (state_ == State::kWaitForRootObject) { state_ = State::kWaitForRootPartitions; - return continue_parsing_; + return CanContinue(ec); } if (state_ != State::kWaitForNextPartition) { - return false; + return NotSupported(ec); } state_ = State::kProcessingAttribute; - return continue_parsing_; + return CanContinue(ec); } - -bool PartitionsSaxHandler::String(const char* str, unsigned int length, bool) { +bool PartitionsSaxHandler::String(const std::string& str, error_code& ec) { switch (state_) { case State::kProcessingAttribute: - state_ = ProcessNextAttribute(str, length); - return continue_parsing_; + state_ = ProcessNextAttribute(str); + return CanContinue(ec); - case State::kWaitForRootPartitions: - if (HashStringToInt("partitions") == HashStringToInt(str)) { + case State::kWaitForRootPartitions: { + if (HashStringToInt("partitions") == HashStringToInt(str.c_str())) { state_ = State::kWaitPartitionsArray; - return continue_parsing_; - } else { - return false; + return CanContinue(ec); } + return NotSupported(ec); + } case State::kParsingPartitionName: - partition_.SetPartition(std::string(str, length)); + partition_.SetPartition(str); break; case State::kParsingDataHandle: - partition_.SetDataHandle(std::string(str, length)); + partition_.SetDataHandle(str); break; case State::kParsingChecksum: - partition_.SetChecksum(std::string(str, length)); + partition_.SetChecksum(str); break; case State::kParsingCrc: - partition_.SetCrc(std::string(str, length)); + partition_.SetCrc(str); break; case State::kParsingIgnoreAttribute: break; @@ -92,10 +91,11 @@ bool PartitionsSaxHandler::String(const char* str, unsigned int length, bool) { state_ = State::kProcessingAttribute; - return continue_parsing_; + return CanContinue(ec); } -bool PartitionsSaxHandler::Uint(unsigned int value) { +bool PartitionsSaxHandler::on_int64(const int64_t value, string_view, + error_code& ec) { if (state_ == State::kParsingVersion) { partition_.SetVersion(value); } else if (state_ == State::kParsingDataSize) { @@ -103,61 +103,60 @@ bool PartitionsSaxHandler::Uint(unsigned int value) { } else if (state_ == State::kParsingCompressedDataSize) { partition_.SetCompressedDataSize(value); } else { - return false; + return NotSupported(ec); } state_ = State::kProcessingAttribute; - return continue_parsing_; + return CanContinue(ec); } -bool PartitionsSaxHandler::EndObject(unsigned int) { +bool PartitionsSaxHandler::on_object_end(std::size_t, error_code& ec) { if (state_ == State::kWaitForRootObjectEnd) { state_ = State::kParsingComplete; - return true; // complete + return CanContinue(ec); // complete } if (state_ != State::kProcessingAttribute) { - return false; + return NotSupported(ec); } if (partition_.GetDataHandle().empty() || partition_.GetPartition().empty()) { - return false; // partition is not valid + return NotSupported(ec); // partition is not valid } partition_callback_(std::move(partition_)); state_ = State::kWaitForNextPartition; - return continue_parsing_; + return CanContinue(ec); } -bool PartitionsSaxHandler::StartArray() { +bool PartitionsSaxHandler::on_array_begin(boost::json::error_code& ec) { // We expect only a single array in whol response if (state_ != State::kWaitPartitionsArray) { - return false; + return NotSupported(ec); } state_ = State::kWaitForNextPartition; - return continue_parsing_; + return CanContinue(ec); } -bool PartitionsSaxHandler::EndArray(unsigned int) { +bool PartitionsSaxHandler::on_array_end(std::size_t, + boost::json::error_code& ec) { if (state_ != State::kWaitForNextPartition) { - return false; + return NotSupported(ec); } state_ = State::kWaitForRootObjectEnd; - return continue_parsing_; + return CanContinue(ec); } -bool PartitionsSaxHandler::Default() { return false; } - void PartitionsSaxHandler::Abort() { continue_parsing_.store(false); } PartitionsSaxHandler::State PartitionsSaxHandler::ProcessNextAttribute( - const char* name, unsigned int /*length*/) { - switch (HashStringToInt(name)) { + const std::string& name) { + switch (HashStringToInt(name.c_str())) { case HashStringToInt("dataHandle"): return State::kParsingDataHandle; case HashStringToInt("partition"): diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.h b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.h index 1e5f1f905..f638eb631 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.h +++ b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsSaxHandler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 HERE Europe B.V. + * Copyright (C) 2023-2026 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,9 @@ #include #include -#include "rapidjson/reader.h" +#include +#include +#include #include @@ -31,31 +33,86 @@ namespace dataservice { namespace read { namespace repository { -class PartitionsSaxHandler - : public rapidjson::BaseReaderHandler, - PartitionsSaxHandler> { +class PartitionsSaxHandler { public: using PartitionCallback = std::function; + using string_view = boost::json::string_view; + using error_code = boost::json::error_code; explicit PartitionsSaxHandler(PartitionCallback partition_callback); - /// Json object events - bool StartObject(); - bool EndObject(unsigned int); + /// Abort parsing + void Abort(); - /// Json array events - bool StartArray(); - bool EndArray(unsigned int); + // boost::json::basic_parser handler object methods + static constexpr std::size_t max_array_size = -1; + static constexpr std::size_t max_object_size = -1; + static constexpr std::size_t max_string_size = -1; + static constexpr std::size_t max_key_size = -1; - /// Json attributes events - bool String(const char* str, unsigned int length, bool); - bool Uint(unsigned int value); - bool Default(); + bool on_document_begin(error_code&) { return continue_parsing_; } + bool on_document_end(error_code&) { return continue_parsing_; } - /// Abort parsing - void Abort(); + bool on_array_begin(error_code& ec); + bool on_array_end(std::size_t size, error_code& ec); + + bool on_object_begin(error_code& ec); + bool on_object_end(std::size_t size, error_code& ec); + + bool on_string_part(string_view str, std::size_t size, error_code& ec) { + str.length() == size ? value_ = str : value_ += str; + return CanContinue(ec); + } + + bool on_string(string_view str, std::size_t size, error_code& ec) { + str.length() == size ? value_ = str : value_ += str; + return String(value_, ec); + } + + bool on_key_part(string_view str, std::size_t size, error_code& ec) { + str.length() == size ? key_ = str : key_ += str; + return CanContinue(ec); + } + + bool on_key(string_view str, std::size_t size, error_code& ec) { + str.length() == size ? key_ = str : key_ += str; + return String(key_, ec); + } + + bool on_number_part(string_view, error_code& ec) const { + return CanContinue(ec); + } + + bool on_uint64(uint64_t value, string_view string, error_code& ec) { + return on_int64(static_cast(value), string, ec); + } + + bool on_int64(int64_t value, string_view, error_code& ec); + + static bool on_double(double, string_view, error_code& ec) { + return NotSupported(ec); + } + + static bool on_bool(bool, error_code& ec) { return NotSupported(ec); } + static bool on_null(error_code& ec) { return NotSupported(ec); } + + bool on_comment_part(string_view, error_code& ec) const { + return CanContinue(ec); + } + + bool on_comment(string_view, error_code& ec) const { return CanContinue(ec); } private: + bool String(const std::string& str, error_code& ec); + + bool CanContinue(error_code& ec) const { + return continue_parsing_ || NotSupported(ec); + } + + static bool NotSupported(error_code& ec) { + ec = boost::json::error::extra_data; + return false; + } enum class State { kWaitForRootObject, kWaitForRootPartitions, @@ -77,11 +134,13 @@ class PartitionsSaxHandler kParsingComplete, }; - State ProcessNextAttribute(const char* name, unsigned int length); + static State ProcessNextAttribute(const std::string& name); State state_{State::kWaitForRootObject}; model::Partition partition_; PartitionCallback partition_callback_; + std::string key_; + std::string value_; std::atomic_bool continue_parsing_{true}; }; diff --git a/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp b/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp index 45ad09db0..aa8725381 100644 --- a/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp +++ b/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 HERE Europe B.V. + * Copyright (C) 2023-2026 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -80,6 +80,18 @@ TEST(AsyncJsonStreamTest, NormalFlow) { EXPECT_TRUE(new_current_stream->ReadEmpty()); stream.ResetStream("4", 1); EXPECT_TRUE(new_current_stream->ReadEmpty()); + + { + repository::AsyncJsonStream json_stream; + json_stream.AppendContent("123", 4); + auto current_json_stream = json_stream.GetCurrentStream(); + auto view = current_json_stream->ReadView(); + EXPECT_EQ(view.size(), 3); + EXPECT_FALSE(current_json_stream->ReadEmpty()); + EXPECT_EQ(current_json_stream->Peek(), '\0'); + EXPECT_EQ(current_json_stream->Take(), '\0'); + EXPECT_TRUE(current_json_stream->ReadEmpty()); + } } } // namespace diff --git a/olp-cpp-sdk-dataservice-read/tests/PartitionsSaxHandlerTest.cpp b/olp-cpp-sdk-dataservice-read/tests/PartitionsSaxHandlerTest.cpp index 94c340392..cb995dcfb 100644 --- a/olp-cpp-sdk-dataservice-read/tests/PartitionsSaxHandlerTest.cpp +++ b/olp-cpp-sdk-dataservice-read/tests/PartitionsSaxHandlerTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 HERE Europe B.V. + * Copyright (C) 2023-2026 HERE Europe B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,36 +47,60 @@ unsigned int len(const char* str) { TEST(PartitionsSaxHandlerTest, NormalFlow) { model::Partition parsed_partition; - auto callback = [&](model::Partition partition) { + auto callback = [&](const model::Partition& partition) { parsed_partition = partition; }; repository::PartitionsSaxHandler handler(callback); - ASSERT_TRUE(handler.StartObject()); - ASSERT_TRUE(handler.String(kPartitions, len(kPartitions), true)); - ASSERT_TRUE(handler.StartArray()); - - ASSERT_TRUE(handler.StartObject()); - ASSERT_TRUE(handler.String(kDataHandle, len(kDataHandle), true)); - ASSERT_TRUE(handler.String(kDataHandleValue, len(kDataHandleValue), true)); - ASSERT_TRUE(handler.String(kPartition, len(kPartition), true)); - ASSERT_TRUE(handler.String(kPartitionValue, len(kPartitionValue), true)); - ASSERT_TRUE(handler.String(kChecksum, len(kChecksum), true)); - ASSERT_TRUE(handler.String(kChecksumValue, len(kChecksumValue), true)); - ASSERT_TRUE(handler.String(kDataSize, len(kDataSize), true)); - ASSERT_TRUE(handler.Uint(150)); + boost::json::error_code error_code; + ASSERT_TRUE(handler.on_object_begin(error_code)); + ASSERT_TRUE(handler.on_key(kPartitions, len(kPartitions), error_code)); + ASSERT_TRUE(handler.on_array_begin(error_code)); + ASSERT_TRUE(handler.on_object_begin(error_code)); + + boost::json::string_view key_part_1{kDataHandle, 2}; + boost::json::string_view key_part_2{kDataHandle + key_part_1.size(), 3}; + boost::json::string_view key_part_last{ + kDataHandle + key_part_1.size() + key_part_2.size(), + len(kDataHandle) - key_part_1.size() - key_part_2.size()}; + ASSERT_TRUE(handler.on_key_part(key_part_1, key_part_1.size(), error_code)); + ASSERT_TRUE(handler.on_key_part( + key_part_2, key_part_1.size() + key_part_2.size(), error_code)); + ASSERT_TRUE(handler.on_key(key_part_last, len(kDataHandle), error_code)); + + boost::json::string_view value_part_1{kDataHandleValue, 2}; + boost::json::string_view value_part_2{kDataHandleValue + value_part_1.size(), + 3}; + boost::json::string_view value_part_last{ + kDataHandleValue + value_part_1.size() + value_part_2.size(), + len(kDataHandleValue) - value_part_1.size() - value_part_2.size()}; ASSERT_TRUE( - handler.String(kCompressedDataSize, len(kCompressedDataSize), true)); - ASSERT_TRUE(handler.Uint(100)); - ASSERT_TRUE(handler.String(kVersion, len(kVersion), true)); - ASSERT_TRUE(handler.Uint(6)); - ASSERT_TRUE(handler.String(kCrc, len(kCrc), true)); - ASSERT_TRUE(handler.String(kCrcValue, len(kCrcValue), true)); - ASSERT_TRUE(handler.EndObject(0)); + handler.on_string_part(value_part_1, value_part_1.size(), error_code)); + ASSERT_TRUE(handler.on_string_part( + value_part_2, value_part_1.size() + value_part_2.size(), error_code)); + ASSERT_TRUE( + handler.on_string(value_part_last, len(kDataHandleValue), error_code)); - ASSERT_TRUE(handler.EndArray(0)); - ASSERT_TRUE(handler.EndObject(0)); + ASSERT_TRUE(handler.on_key(kPartition, len(kPartition), error_code)); + ASSERT_TRUE( + handler.on_string(kPartitionValue, len(kPartitionValue), error_code)); + ASSERT_TRUE(handler.on_key(kChecksum, len(kChecksum), error_code)); + ASSERT_TRUE( + handler.on_string(kChecksumValue, len(kChecksumValue), error_code)); + ASSERT_TRUE(handler.on_key(kDataSize, len(kDataSize), error_code)); + ASSERT_TRUE(handler.on_uint64(150, "150", error_code)); + ASSERT_TRUE(handler.on_key(kCompressedDataSize, len(kCompressedDataSize), + error_code)); + ASSERT_TRUE(handler.on_uint64(100, "100", error_code)); + ASSERT_TRUE(handler.on_key(kVersion, len(kVersion), error_code)); + ASSERT_TRUE(handler.on_uint64(6, "6", error_code)); + ASSERT_TRUE(handler.on_key(kCrc, len(kCrc), error_code)); + ASSERT_TRUE(handler.on_string(kCrcValue, len(kCrcValue), error_code)); + ASSERT_TRUE(handler.on_object_end(0, error_code)); + + ASSERT_TRUE(handler.on_array_end(0, error_code)); + ASSERT_TRUE(handler.on_object_end(0, error_code)); EXPECT_EQ(parsed_partition.GetDataHandle(), std::string(kDataHandleValue)); EXPECT_EQ(parsed_partition.GetPartition(), std::string(kPartitionValue)); @@ -94,92 +118,95 @@ TEST(PartitionsSaxHandlerTest, WrongJSONStructure) { auto callback = [&](model::Partition) {}; repository::PartitionsSaxHandler handler(callback); + boost::json::error_code error_code; // Initial state expects an object - ASSERT_FALSE(handler.String(kPartitions, len(kPartitions), true)); - ASSERT_FALSE(handler.Uint(6)); - ASSERT_FALSE(handler.StartArray()); - ASSERT_FALSE(handler.EndArray(0)); - ASSERT_FALSE(handler.EndObject(0)); + ASSERT_FALSE(handler.on_key(kPartitions, len(kPartitions), error_code)); + ASSERT_FALSE(handler.on_uint64(6, "6", error_code)); + ASSERT_FALSE(handler.on_array_begin(error_code)); + ASSERT_FALSE(handler.on_array_end(0, error_code)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); - ASSERT_TRUE(handler.StartObject()); + ASSERT_TRUE(handler.on_object_begin(error_code)); // next state expects a partitions string - ASSERT_FALSE(handler.String(kDataHandle, len(kDataHandle), true)); - ASSERT_FALSE(handler.Uint(6)); - ASSERT_FALSE(handler.StartArray()); - ASSERT_FALSE(handler.StartObject()); - ASSERT_FALSE(handler.EndObject(0)); - ASSERT_FALSE(handler.EndArray(0)); + ASSERT_FALSE(handler.on_key(kDataHandle, len(kDataHandle), error_code)); + ASSERT_FALSE(handler.on_uint64(6, "6", error_code)); + ASSERT_FALSE(handler.on_array_begin(error_code)); + ASSERT_FALSE(handler.on_object_begin(error_code)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); + ASSERT_FALSE(handler.on_array_end(0, error_code)); - ASSERT_TRUE(handler.String(kPartitions, len(kPartitions), true)); + ASSERT_TRUE(handler.on_key(kPartitions, len(kPartitions), error_code)); // expect partitions array - ASSERT_FALSE(handler.String(kDataHandle, len(kDataHandle), true)); - ASSERT_FALSE(handler.Uint(6)); - ASSERT_FALSE(handler.StartObject()); - ASSERT_FALSE(handler.EndObject(0)); - ASSERT_FALSE(handler.EndArray(0)); + ASSERT_FALSE(handler.on_key(kDataHandle, len(kDataHandle), error_code)); + ASSERT_FALSE(handler.on_uint64(6, "6", error_code)); + ASSERT_FALSE(handler.on_object_begin(error_code)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); + ASSERT_FALSE(handler.on_array_end(0, error_code)); - ASSERT_TRUE(handler.StartArray()); + ASSERT_TRUE(handler.on_array_begin(error_code)); // expect partition object - ASSERT_FALSE(handler.String(kDataHandle, len(kDataHandle), true)); - ASSERT_FALSE(handler.Uint(6)); - ASSERT_FALSE(handler.StartArray()); - ASSERT_FALSE(handler.EndObject(0)); + ASSERT_FALSE(handler.on_key(kDataHandle, len(kDataHandle), error_code)); + ASSERT_FALSE(handler.on_uint64(6, "6", error_code)); + ASSERT_FALSE(handler.on_array_begin(error_code)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); - ASSERT_TRUE(handler.StartObject()); + ASSERT_TRUE(handler.on_object_begin(error_code)); // object is not valid - ASSERT_FALSE(handler.EndObject(0)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); // expect partition attribute - ASSERT_FALSE(handler.Uint(6)); - ASSERT_FALSE(handler.StartArray()); + ASSERT_FALSE(handler.on_uint64(6, "6", error_code)); + ASSERT_FALSE(handler.on_array_begin(error_code)); - ASSERT_TRUE(handler.String(kDataHandle, len(kDataHandle), true)); + ASSERT_TRUE(handler.on_key(kDataHandle, len(kDataHandle), error_code)); // expect string attribute value - ASSERT_FALSE(handler.Uint(6)); - ASSERT_FALSE(handler.StartArray()); - ASSERT_FALSE(handler.EndObject(0)); + ASSERT_FALSE(handler.on_uint64(6, "6", error_code)); + ASSERT_FALSE(handler.on_array_begin(error_code)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); - ASSERT_TRUE(handler.String(kDataHandleValue, len(kDataHandleValue), true)); + ASSERT_TRUE( + handler.on_string(kDataHandleValue, len(kDataHandleValue), error_code)); // object is not valid - ASSERT_FALSE(handler.EndObject(0)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); // integer properties - ASSERT_TRUE(handler.String(kDataSize, len(kDataSize), true)); + ASSERT_TRUE(handler.on_key(kDataSize, len(kDataSize), error_code)); - ASSERT_FALSE(handler.String(kDataHandle, len(kDataHandle), true)); - ASSERT_FALSE(handler.StartArray()); - ASSERT_FALSE(handler.EndArray(0)); - ASSERT_FALSE(handler.StartObject()); - ASSERT_FALSE(handler.EndObject(0)); + ASSERT_FALSE(handler.on_string(kDataHandle, len(kDataHandle), error_code)); + ASSERT_FALSE(handler.on_array_begin(error_code)); + ASSERT_FALSE(handler.on_array_end(0, error_code)); + ASSERT_FALSE(handler.on_object_begin(error_code)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); - ASSERT_TRUE(handler.Uint(6)); + ASSERT_TRUE(handler.on_uint64(6, "6", error_code)); - ASSERT_TRUE(handler.String(kPartition, len(kPartition), true)); - ASSERT_TRUE(handler.String(kPartitionValue, len(kPartitionValue), true)); + ASSERT_TRUE(handler.on_key(kPartition, len(kPartition), error_code)); + ASSERT_TRUE( + handler.on_string(kPartitionValue, len(kPartitionValue), error_code)); // complete partition - ASSERT_TRUE(handler.EndObject(0)); + ASSERT_TRUE(handler.on_object_end(0, error_code)); // complete partitions array - ASSERT_TRUE(handler.EndArray(0)); + ASSERT_TRUE(handler.on_array_end(0, error_code)); // complete the json - ASSERT_TRUE(handler.EndObject(0)); + ASSERT_TRUE(handler.on_object_end(0, error_code)); // nothing works anymore - ASSERT_FALSE(handler.String(kDataHandle, len(kDataHandle), true)); - ASSERT_FALSE(handler.Uint(6)); - ASSERT_FALSE(handler.StartArray()); - ASSERT_FALSE(handler.EndArray(0)); - ASSERT_FALSE(handler.StartObject()); - ASSERT_FALSE(handler.EndObject(0)); + ASSERT_FALSE(handler.on_key(kDataHandle, len(kDataHandle), error_code)); + ASSERT_FALSE(handler.on_uint64(6, "6", error_code)); + ASSERT_FALSE(handler.on_array_begin(error_code)); + ASSERT_FALSE(handler.on_array_end(0, error_code)); + ASSERT_FALSE(handler.on_object_begin(error_code)); + ASSERT_FALSE(handler.on_object_end(0, error_code)); } } // namespace From 89621ee9b323ce7688b4c5e465c492bb8c2935cd Mon Sep 17 00:00:00 2001 From: Alexander Sopov Date: Fri, 27 Feb 2026 13:34:09 +0100 Subject: [PATCH 2/6] Refactor PartitionsSaxHandler to use boost::json Previously it was based on rapidjson The tests are adjusted accordingly. Relates-To: OCMAM-448 Signed-off-by: Alexander Sopov --- olp-cpp-sdk-dataservice-read/CMakeLists.txt | 8 -------- .../src/repositories/PartitionsRepository.cpp | 1 - 2 files changed, 9 deletions(-) diff --git a/olp-cpp-sdk-dataservice-read/CMakeLists.txt b/olp-cpp-sdk-dataservice-read/CMakeLists.txt index b78da3102..b0e8c05c4 100644 --- a/olp-cpp-sdk-dataservice-read/CMakeLists.txt +++ b/olp-cpp-sdk-dataservice-read/CMakeLists.txt @@ -21,21 +21,13 @@ set(DESCRIPTION "C++ API library for reading OLP data") file(GLOB_RECURSE INC "include/*.h*") file(GLOB_RECURSE SRC "src/*.*") -find_package(Boost REQUIRED) - add_library(${PROJECT_NAME} ${SRC} ${INC}) -target_compile_definitions(${PROJECT_NAME} - PRIVATE - BOOST_ALL_NO_LIB - BOOST_JSON_NO_LIB) - target_include_directories(${PROJECT_NAME} PUBLIC $ - $ $ PRIVATE $ PRIVATE ${olp-cpp-sdk-core_INCLUDE_DIRS}) diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp index 895610f28..d8b3942eb 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp +++ b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include "CatalogRepository.h" #include "generated/api/MetadataApi.h" #include "generated/api/QueryApi.h" From 0dd731329360d5d39c7a209ac67a7fcbfac4c36a Mon Sep 17 00:00:00 2001 From: Alexander Sopov Date: Fri, 27 Feb 2026 13:45:30 +0100 Subject: [PATCH 3/6] Refactor PartitionsSaxHandler to use boost::json Previously it was based on rapidjson The tests are adjusted accordingly. Relates-To: OCMAM-448 Signed-off-by: Alexander Sopov --- .../tests/AsyncJsonStreamTest.cpp | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp b/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp index aa8725381..238444400 100644 --- a/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp +++ b/olp-cpp-sdk-dataservice-read/tests/AsyncJsonStreamTest.cpp @@ -40,6 +40,15 @@ TEST(AsyncJsonStreamTest, NormalFlow) { EXPECT_EQ(current_stream->Tell(), 2u); EXPECT_EQ(current_stream->Take(), '3'); + stream.ResetStream("123", 4); + current_stream = stream.GetCurrentStream(); + auto view = current_stream->ReadView(); + EXPECT_EQ(view.size(), 3); + EXPECT_FALSE(current_stream->ReadEmpty()); + EXPECT_EQ(current_stream->Peek(), '\0'); + EXPECT_EQ(current_stream->Take(), '\0'); + EXPECT_TRUE(current_stream->ReadEmpty()); + stream.ResetStream("234", 3); auto new_current_stream = stream.GetCurrentStream(); @@ -80,18 +89,6 @@ TEST(AsyncJsonStreamTest, NormalFlow) { EXPECT_TRUE(new_current_stream->ReadEmpty()); stream.ResetStream("4", 1); EXPECT_TRUE(new_current_stream->ReadEmpty()); - - { - repository::AsyncJsonStream json_stream; - json_stream.AppendContent("123", 4); - auto current_json_stream = json_stream.GetCurrentStream(); - auto view = current_json_stream->ReadView(); - EXPECT_EQ(view.size(), 3); - EXPECT_FALSE(current_json_stream->ReadEmpty()); - EXPECT_EQ(current_json_stream->Peek(), '\0'); - EXPECT_EQ(current_json_stream->Take(), '\0'); - EXPECT_TRUE(current_json_stream->ReadEmpty()); - } } } // namespace From 8321fefc34f48cc702a16c64bba41c9e54fb2f3a Mon Sep 17 00:00:00 2001 From: Alexander Sopov Date: Fri, 27 Feb 2026 14:01:19 +0100 Subject: [PATCH 4/6] Refactor PartitionsSaxHandler to use boost::json Previously it was based on rapidjson The tests are adjusted accordingly. Relates-To: OCMAM-448 Signed-off-by: Alexander Sopov --- olp-cpp-sdk-core/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/olp-cpp-sdk-core/CMakeLists.txt b/olp-cpp-sdk-core/CMakeLists.txt index e694436ad..5b6e2cb3c 100644 --- a/olp-cpp-sdk-core/CMakeLists.txt +++ b/olp-cpp-sdk-core/CMakeLists.txt @@ -457,8 +457,8 @@ if (OLP_SDK_USE_STD_ANY) PUBLIC OLP_SDK_USE_STD_ANY) endif() -target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_ALL_NO_LIB) -target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_JSON_NO_LIB) +target_compile_definitions(${PROJECT_NAME} PUBLIC BOOST_ALL_NO_LIB) +target_compile_definitions(${PROJECT_NAME} PUBLIC BOOST_JSON_NO_LIB) target_include_directories(${PROJECT_NAME} PUBLIC $ From a2df8f4036f74ba4b05f47da7e19fdb61bca2b84 Mon Sep 17 00:00:00 2001 From: Alexander Sopov Date: Fri, 27 Feb 2026 14:11:52 +0100 Subject: [PATCH 5/6] Refactor PartitionsSaxHandler to use boost::json Previously it was based on rapidjson The tests are adjusted accordingly. Relates-To: OCMAM-448 Signed-off-by: Alexander Sopov --- .../src/repositories/AsyncJsonStream.cpp | 5 ++--- .../src/repositories/PartitionsRepository.cpp | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp b/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp index bd5549f1b..8bd6e1e81 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp +++ b/olp-cpp-sdk-dataservice-read/src/repositories/AsyncJsonStream.cpp @@ -35,10 +35,9 @@ boost::json::string_view RapidJsonByteStream::ReadView() { if (ReadEmpty()) { SwapBuffers(); } - auto terminator_it = - std::find(read_buffer_.begin() + count_, read_buffer_.end(), '\0'); auto begin = read_buffer_.begin() + count_; - boost::core::basic_string_view::size_type size = + auto terminator_it = std::find(begin, read_buffer_.end(), '\0'); + boost::json::string_view::size_type size = std::distance(begin, terminator_it); count_ += size; full_count_ += size; diff --git a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp index d8b3942eb..7346e37c9 100644 --- a/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp +++ b/olp-cpp-sdk-dataservice-read/src/repositories/PartitionsRepository.cpp @@ -660,7 +660,6 @@ client::ApiNoResponse PartitionsRepository::ParsePartitionsStream( auto reader_cancellation_token = client::CancellationToken([parser, &async_stream]() { - // partitions_handler->Abort(); parser->handler().Abort(); async_stream->CloseStream(client::ApiError::Cancelled()); }); From becba7d7b74e28ef1b675b89b001aab9a394d736 Mon Sep 17 00:00:00 2001 From: Alexander Sopov Date: Fri, 27 Feb 2026 14:35:35 +0100 Subject: [PATCH 6/6] Refactor PartitionsSaxHandler to use boost::json Previously it was based on rapidjson The tests are adjusted accordingly. Relates-To: OCMAM-448 Signed-off-by: Alexander Sopov --- olp-cpp-sdk-core/CMakeLists.txt | 4 ++-- olp-cpp-sdk-dataservice-read/CMakeLists.txt | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/olp-cpp-sdk-core/CMakeLists.txt b/olp-cpp-sdk-core/CMakeLists.txt index 5b6e2cb3c..e694436ad 100644 --- a/olp-cpp-sdk-core/CMakeLists.txt +++ b/olp-cpp-sdk-core/CMakeLists.txt @@ -457,8 +457,8 @@ if (OLP_SDK_USE_STD_ANY) PUBLIC OLP_SDK_USE_STD_ANY) endif() -target_compile_definitions(${PROJECT_NAME} PUBLIC BOOST_ALL_NO_LIB) -target_compile_definitions(${PROJECT_NAME} PUBLIC BOOST_JSON_NO_LIB) +target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_ALL_NO_LIB) +target_compile_definitions(${PROJECT_NAME} PRIVATE BOOST_JSON_NO_LIB) target_include_directories(${PROJECT_NAME} PUBLIC $ diff --git a/olp-cpp-sdk-dataservice-read/CMakeLists.txt b/olp-cpp-sdk-dataservice-read/CMakeLists.txt index b0e8c05c4..290cac44c 100644 --- a/olp-cpp-sdk-dataservice-read/CMakeLists.txt +++ b/olp-cpp-sdk-dataservice-read/CMakeLists.txt @@ -25,6 +25,11 @@ add_library(${PROJECT_NAME} ${SRC} ${INC}) +target_compile_definitions(${PROJECT_NAME} + PRIVATE + BOOST_ALL_NO_LIB + BOOST_JSON_NO_LIB) + target_include_directories(${PROJECT_NAME} PUBLIC $