diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index 74266d0037..e590d38117 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -408,6 +408,7 @@ endif() set(ecal_util_src src/util/ecal_expmap.h src/util/ecal_thread.h + src/util/expanding_vector.h src/util/frequency_calculator.h src/util/getenvvar.h ) diff --git a/ecal/core/include/ecal/ecal_types.h b/ecal/core/include/ecal/ecal_types.h index 2e1034e339..e446c25bd4 100644 --- a/ecal/core/include/ecal/ecal_types.h +++ b/ecal/core/include/ecal/ecal_types.h @@ -54,6 +54,13 @@ namespace eCAL { return std::tie(name, encoding, descriptor) < std::tie(rhs.name, rhs.encoding, rhs.descriptor); } + + void clear() + { + name.clear(); + encoding.clear(); + descriptor.clear(); + } //!< @endcond }; diff --git a/ecal/core/src/pubsub/ecal_pubgate.cpp b/ecal/core/src/pubsub/ecal_pubgate.cpp index 8673594ab1..1ca992191c 100644 --- a/ecal/core/src/pubsub/ecal_pubgate.cpp +++ b/ecal/core/src/pubsub/ecal_pubgate.cpp @@ -197,7 +197,7 @@ namespace eCAL const std::shared_lock lock(m_topic_name_datawriter_sync); for (const auto& iter : m_topic_name_datawriter_map) { - reg_sample_list_.samples.emplace_back(iter.second->GetRegistration()); + iter.second->GetRegistration(reg_sample_list_.push_back()); } } } diff --git a/ecal/core/src/pubsub/ecal_subgate.cpp b/ecal/core/src/pubsub/ecal_subgate.cpp index fd1343d082..3d0366e1c7 100644 --- a/ecal/core/src/pubsub/ecal_subgate.cpp +++ b/ecal/core/src/pubsub/ecal_subgate.cpp @@ -289,7 +289,7 @@ namespace eCAL const std::shared_lock lock(m_topic_name_datareader_sync); for (const auto& iter : m_topic_name_datareader_map) { - reg_sample_list_.samples.emplace_back(iter.second->GetRegistration()); + iter.second->GetRegistration(reg_sample_list_.push_back()); } } } diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/ecal/core/src/readwrite/ecal_reader.cpp index 975470d178..1f600e4225 100644 --- a/ecal/core/src/readwrite/ecal_reader.cpp +++ b/ecal/core/src/readwrite/ecal_reader.cpp @@ -594,7 +594,9 @@ namespace eCAL void CDataReader::Register() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); + Registration::Sample sample; + GetRegistrationSample(sample); + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(sample); #ifndef NDEBUG // log it @@ -606,7 +608,9 @@ namespace eCAL void CDataReader::Unregister() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); + Registration::Sample sample; + GetUnregistrationSample(sample); + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(sample); #ifndef NDEBUG // log it @@ -615,10 +619,10 @@ namespace eCAL #endif // ECAL_CORE_REGISTRATION } - Registration::Sample CDataReader::GetRegistration() + void CDataReader::GetRegistration(Registration::Sample& sample) { // return registration - return GetRegistrationSample(); + return GetRegistrationSample(sample); } bool CDataReader::IsPublished() const @@ -631,10 +635,8 @@ namespace eCAL return m_connection_count; } - Registration::Sample CDataReader::GetRegistrationSample() + void CDataReader::GetRegistrationSample(Registration::Sample& ecal_reg_sample) { - // create registration sample - Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_subscriber; auto& ecal_reg_sample_identifier = ecal_reg_sample.identifier; @@ -706,14 +708,10 @@ namespace eCAL // we do not know the number of connections .. ecal_reg_sample_topic.connections_loc = 0; ecal_reg_sample_topic.connections_ext = 0; - - return ecal_reg_sample; } - Registration::Sample CDataReader::GetUnregistrationSample() + void CDataReader::GetUnregistrationSample(Registration::Sample& ecal_unreg_sample) { - // create unregistration sample - Registration::Sample ecal_unreg_sample; ecal_unreg_sample.cmd_type = bct_unreg_subscriber; auto& ecal_reg_sample_identifier = ecal_unreg_sample.identifier; @@ -726,8 +724,6 @@ namespace eCAL ecal_reg_sample_topic.pname = m_pname; ecal_reg_sample_topic.tname = m_topic_name; ecal_reg_sample_topic.uname = Process::GetUnitName(); - - return ecal_unreg_sample; } void CDataReader::StartTransportLayer() diff --git a/ecal/core/src/readwrite/ecal_reader.h b/ecal/core/src/readwrite/ecal_reader.h index 7001348366..e6341295fc 100644 --- a/ecal/core/src/readwrite/ecal_reader.h +++ b/ecal/core/src/readwrite/ecal_reader.h @@ -86,7 +86,7 @@ namespace eCAL void ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_); - Registration::Sample GetRegistration(); + void GetRegistration(Registration::Sample& sample); bool IsCreated() const { return(m_created); } bool IsPublished() const; @@ -115,8 +115,8 @@ namespace eCAL void Register(); void Unregister(); - Registration::Sample GetRegistrationSample(); - Registration::Sample GetUnregistrationSample(); + void GetRegistrationSample(Registration::Sample& sample); + void GetUnregistrationSample(Registration::Sample& sample); void StartTransportLayer(); void StopTransportLayer(); diff --git a/ecal/core/src/readwrite/ecal_writer.cpp b/ecal/core/src/readwrite/ecal_writer.cpp index b894334037..eb120a9c3e 100644 --- a/ecal/core/src/readwrite/ecal_writer.cpp +++ b/ecal/core/src/readwrite/ecal_writer.cpp @@ -628,7 +628,9 @@ namespace eCAL void CDataWriter::Register() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); + Registration::Sample registration_sample; + GetRegistrationSample(registration_sample); + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(registration_sample); #ifndef NDEBUG // log it @@ -640,7 +642,9 @@ namespace eCAL void CDataWriter::Unregister() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); + Registration::Sample unregistration_sample; + GetUnregistrationSample(unregistration_sample); + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(unregistration_sample); #ifndef NDEBUG // log it @@ -649,15 +653,13 @@ namespace eCAL #endif // ECAL_CORE_REGISTRATION } - Registration::Sample CDataWriter::GetRegistration() + void CDataWriter::GetRegistration(Registration::Sample& sample) { - return GetRegistrationSample(); + GetRegistrationSample(sample); } - Registration::Sample CDataWriter::GetRegistrationSample() + void CDataWriter::GetRegistrationSample(Registration::Sample& ecal_reg_sample) { - // create registration sample - Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_publisher; auto& ecal_reg_sample_identifier = ecal_reg_sample.identifier; @@ -748,14 +750,10 @@ namespace eCAL } ecal_reg_sample_topic.connections_loc = static_cast(loc_connections); ecal_reg_sample_topic.connections_ext = static_cast(ext_connections); - - return ecal_reg_sample; } - Registration::Sample CDataWriter::GetUnregistrationSample() + void CDataWriter::GetUnregistrationSample(Registration::Sample& ecal_unreg_sample) { - // create unregistration sample - Registration::Sample ecal_unreg_sample; ecal_unreg_sample.cmd_type = bct_unreg_publisher; auto& ecal_reg_sample_identifier = ecal_unreg_sample.identifier; @@ -768,8 +766,6 @@ namespace eCAL ecal_reg_sample_topic.pname = m_pname; ecal_reg_sample_topic.tname = m_topic_name; ecal_reg_sample_topic.uname = Process::GetUnitName(); - - return ecal_unreg_sample; } void CDataWriter::FireConnectEvent(const std::string& tid_, const SDataTypeInformation& tinfo_) diff --git a/ecal/core/src/readwrite/ecal_writer.h b/ecal/core/src/readwrite/ecal_writer.h index 8009055f4d..d7590013d7 100644 --- a/ecal/core/src/readwrite/ecal_writer.h +++ b/ecal/core/src/readwrite/ecal_writer.h @@ -92,7 +92,7 @@ namespace eCAL void ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& sub_layer_states_, const std::string& reader_par_); void RemoveSubscription(const SSubscriptionInfo& subscription_info_); - Registration::Sample GetRegistration(); + void GetRegistration(Registration::Sample& sample); void RefreshSendCounter(); bool IsCreated() const { return(m_created); } @@ -119,8 +119,8 @@ namespace eCAL void Register(); void Unregister(); - Registration::Sample GetRegistrationSample(); - Registration::Sample GetUnregistrationSample(); + void GetRegistrationSample(Registration::Sample& sample); + void GetUnregistrationSample(Registration::Sample& sample); bool StartUdpLayer(); bool StartShmLayer(); diff --git a/ecal/core/src/registration/ecal_registration_provider.cpp b/ecal/core/src/registration/ecal_registration_provider.cpp index 8121c33b4a..b5e0a62b11 100644 --- a/ecal/core/src/registration/ecal_registration_provider.cpp +++ b/ecal/core/src/registration/ecal_registration_provider.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -136,7 +137,7 @@ namespace eCAL void CRegistrationProvider::AddSingleSample(const Registration::Sample& sample_) { const std::lock_guard lock(m_applied_sample_list_mtx); - m_applied_sample_list.samples.push_back(sample_); + m_applied_sample_list.push_back(sample_); } void CRegistrationProvider::RegisterSendThread() @@ -144,38 +145,39 @@ namespace eCAL // collect all registrations and send them out cyclic { // create sample list - Registration::SampleList sample_list; + m_send_thread_sample_list.clear(); // and add process registration sample - sample_list.samples.push_back(Registration::GetProcessRegisterSample()); + m_send_thread_sample_list.push_back(Registration::GetProcessRegisterSample()); #if ECAL_CORE_SUBSCRIBER // add subscriber registrations - if (g_subgate() != nullptr) g_subgate()->GetRegistrations(sample_list); + if (g_subgate() != nullptr) g_subgate()->GetRegistrations(m_send_thread_sample_list); #endif #if ECAL_CORE_PUBLISHER // add publisher registrations - if (g_pubgate() != nullptr) g_pubgate()->GetRegistrations(sample_list); + if (g_pubgate() != nullptr) g_pubgate()->GetRegistrations(m_send_thread_sample_list); #endif #if ECAL_CORE_SERVICE // add server registrations - if (g_servicegate() != nullptr) g_servicegate()->GetRegistrations(sample_list); + if (g_servicegate() != nullptr) g_servicegate()->GetRegistrations(m_send_thread_sample_list); // add client registrations - if (g_clientgate() != nullptr) g_clientgate()->GetRegistrations(sample_list); + if (g_clientgate() != nullptr) g_clientgate()->GetRegistrations(m_send_thread_sample_list); #endif // append applied samples list to sample list - if (!m_applied_sample_list.samples.empty()) + if (!m_applied_sample_list.empty()) { const std::lock_guard lock(m_applied_sample_list_mtx); - sample_list.samples.splice(sample_list.samples.end(), m_applied_sample_list.samples); + std::copy(m_applied_sample_list.begin(), m_applied_sample_list.end(), std::back_inserter(m_send_thread_sample_list)); + m_applied_sample_list.clear(); } // send collected registration sample list - m_reg_sender->SendSampleList(sample_list); + m_reg_sender->SendSampleList(m_send_thread_sample_list); } } } diff --git a/ecal/core/src/registration/ecal_registration_provider.h b/ecal/core/src/registration/ecal_registration_provider.h index 421d46a0e5..4746311575 100644 --- a/ecal/core/src/registration/ecal_registration_provider.h +++ b/ecal/core/src/registration/ecal_registration_provider.h @@ -65,6 +65,8 @@ namespace eCAL std::mutex m_applied_sample_list_mtx; Registration::SampleList m_applied_sample_list; + Registration::SampleList m_send_thread_sample_list; + Registration::SAttributes m_attributes; }; } diff --git a/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp index c8a51a0153..bb62ded25f 100644 --- a/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp +++ b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp @@ -70,15 +70,17 @@ namespace eCAL void CRegistrationReceiverSHM::Receive() { + // At the moment this function is called synchronously by a dedicated thread. + // If this changes, we need to protect the sample list member variable MemfileBroadcastMessageListT message_list; if (m_memfile_broadcast_reader->Read(message_list, 0)) { - eCAL::Registration::SampleList sample_list; + m_sample_list.clear(); for (const auto& message : message_list) { - if (DeserializeFromBuffer(static_cast(message.data), message.size, sample_list)) + if (DeserializeFromBuffer(static_cast(message.data), message.size, m_sample_list)) { - for (const auto& sample : sample_list.samples) + for (const auto& sample : m_sample_list) { m_apply_sample_callback(sample); } diff --git a/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h index d34f4d53e1..4164e76bf9 100644 --- a/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h +++ b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h @@ -59,6 +59,8 @@ namespace eCAL std::unique_ptr m_memfile_broadcast_reader; std::unique_ptr m_memfile_broadcast_reader_thread; + eCAL::Registration::SampleList m_sample_list; + RegistrationApplySampleCallbackT m_apply_sample_callback; }; } diff --git a/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp b/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp index 936efead72..26efe9c11c 100644 --- a/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp +++ b/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp @@ -58,7 +58,7 @@ namespace eCAL bool CRegistrationSenderUDP::SendSampleList(const Registration::SampleList& sample_list) { bool return_value{ true }; - for (const auto& sample : sample_list.samples) + for (const auto& sample : sample_list) { return_value &= SendSample(sample); } diff --git a/ecal/core/src/serialization/ecal_serialize_common.cpp b/ecal/core/src/serialization/ecal_serialize_common.cpp index 6bb0ff6214..53fcb8d1f3 100644 --- a/ecal/core/src/serialization/ecal_serialize_common.cpp +++ b/ecal/core/src/serialization/ecal_serialize_common.cpp @@ -202,12 +202,12 @@ namespace eCAL /////////////////////////////////////////////// // list /////////////////////////////////////////////// - bool encode_string_list_field(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) + bool encode_string_vector_field(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) { if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* str_list = static_cast*>(*arg); + auto* str_list = static_cast*>(*arg); for (const auto& str : *str_list) { @@ -225,21 +225,20 @@ namespace eCAL return true; } - bool decode_string_list_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + bool decode_string_vector_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) { if (arg == nullptr) return false; if (*arg == nullptr) return false; + auto* tgt_list = static_cast*>(*arg); + auto& tgt_string = tgt_list->push_back(); + size_t len = stream->bytes_left; - std::string tgt_string; tgt_string.resize(len); if (!pb_read(stream, (pb_byte_t*)(tgt_string.data()), tgt_string.size())) // NOLINT(*-pro-type-cstyle-cast) return false; - auto* tgt_list = static_cast*>(*arg); - tgt_list->push_back(tgt_string); - return true; } @@ -251,7 +250,7 @@ namespace eCAL if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* layer_vec = static_cast*>(*arg); + auto* layer_vec = static_cast*>(*arg); for (auto layer : *layer_vec) { @@ -278,7 +277,7 @@ namespace eCAL // shm layer parameter pb_layer.par_layer.has_layer_par_shm = true; - pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.encode = &encode_string_list_field; // NOLINT(*-pro-type-union-access) + pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.encode = &encode_string_vector_field; // NOLINT(*-pro-type-union-access) pb_layer.par_layer.layer_par_shm.memory_file_list.arg = (void*)(&layer.par_layer.layer_par_shm.memory_file_list); if (!pb_encode_submessage(stream, eCAL_pb_TLayer_fields, &pb_layer)) @@ -290,7 +289,7 @@ namespace eCAL return true; } - void encode_registration_layer(pb_callback_t& pb_callback, const std::vector& layer_vec) + void encode_registration_layer(pb_callback_t& pb_callback, const Util::CExpandingVector& layer_vec) { pb_callback.funcs.encode = &encode_registration_layer_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = (void*)(&layer_vec); @@ -302,10 +301,11 @@ namespace eCAL if (*arg == nullptr) return false; eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; - eCAL::Registration::TLayer layer{}; + auto* tgt_vector = static_cast*>(*arg); + auto& layer = tgt_vector->push_back(); // decode shm layer parameter - pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.decode = &decode_string_list_field; // NOLINT(*-pro-type-union-access) + pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.decode = &decode_string_vector_field; // NOLINT(*-pro-type-union-access) pb_layer.par_layer.layer_par_shm.memory_file_list.arg = (void*)(&layer.par_layer.layer_par_shm.memory_file_list); if (!pb_decode(stream, eCAL_pb_TLayer_fields, &pb_layer)) @@ -322,14 +322,10 @@ namespace eCAL // apply tcp layer parameter layer.par_layer.layer_par_tcp.port = pb_layer.par_layer.layer_par_tcp.port; - // add layer - auto* tgt_vector = static_cast*>(*arg); - tgt_vector->push_back(layer); - return true; } - void decode_registration_layer(pb_callback_t& pb_callback, std::vector& layer_vec) + void decode_registration_layer(pb_callback_t& pb_callback, Util::CExpandingVector& layer_vec) { pb_callback.funcs.decode = &decode_registration_layer_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = &layer_vec; @@ -343,7 +339,7 @@ namespace eCAL if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* method_vec = static_cast*>(*arg); + auto* method_vec = static_cast*>(*arg); for (const auto& method : *method_vec) { @@ -369,7 +365,7 @@ namespace eCAL return true; } - void encode_service_methods(pb_callback_t& pb_callback, const std::vector& method_vec) + void encode_service_methods(pb_callback_t& pb_callback, const eCAL::Util::CExpandingVector& method_vec) { pb_callback.funcs.encode = &encode_service_methods_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = (void*)(&method_vec); @@ -381,7 +377,8 @@ namespace eCAL if (*arg == nullptr) return false; eCAL_pb_Method pb_method = eCAL_pb_Method_init_default; - eCAL::Service::Method method{}; + auto* method_vec = static_cast*>(*arg); + auto& method = method_vec->push_back(); // decode method parameter decode_string(pb_method.mname, method.mname); @@ -399,14 +396,10 @@ namespace eCAL // apply method values method.call_count = pb_method.call_count; - // add method to vector - auto* method_vec = static_cast*>(*arg); - method_vec->emplace_back(method); - return true; } - void decode_service_methods(pb_callback_t& pb_callback, std::vector& method_vec) + void decode_service_methods(pb_callback_t& pb_callback, eCAL::Util::CExpandingVector& method_vec) { pb_callback.funcs.decode = &decode_service_methods_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = &method_vec; diff --git a/ecal/core/src/serialization/ecal_serialize_common.h b/ecal/core/src/serialization/ecal_serialize_common.h index 634cc96024..0a38bb7dad 100644 --- a/ecal/core/src/serialization/ecal_serialize_common.h +++ b/ecal/core/src/serialization/ecal_serialize_common.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,10 +56,10 @@ namespace eCAL void encode_map(pb_callback_t& pb_callback, const std::map& str_map); void decode_map(pb_callback_t& pb_callback, std::map& str_map); - void encode_registration_layer(pb_callback_t& pb_callback, const std::vector& layer_vec); - void decode_registration_layer(pb_callback_t& pb_callback, std::vector& layer_vec); + void encode_registration_layer(pb_callback_t& pb_callback, const Util::CExpandingVector& layer_vec); + void decode_registration_layer(pb_callback_t& pb_callback, Util::CExpandingVector& layer_vec); - void encode_service_methods(pb_callback_t& pb_callback, const std::vector& method_vec); - void decode_service_methods(pb_callback_t& pb_callback, std::vector& method_vec); + void encode_service_methods(pb_callback_t& pb_callback, const Util::CExpandingVector& method_vec); + void decode_service_methods(pb_callback_t& pb_callback, Util::CExpandingVector& method_vec); } } diff --git a/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp b/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp index 8390d1e071..761e103bfc 100644 --- a/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp +++ b/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include namespace @@ -505,7 +506,7 @@ namespace if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* sample_list = static_cast*>(*arg); + auto* sample_list = static_cast(*arg); for (const auto& sample : *sample_list) { @@ -535,7 +536,7 @@ namespace // prepare sample for encoding /////////////////////////////////////////////// pb_sample_list_.samples.funcs.encode = &encode_sample_list_field; // NOLINT(*-pro-type-union-access) - pb_sample_list_.samples.arg = (void*)(®istration_list_.samples); + pb_sample_list_.samples.arg = (void*)(®istration_list_); /////////////////////////////////////////////// // evaluate byte size @@ -582,7 +583,11 @@ namespace if (*arg == nullptr) return false; eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; - eCAL::Registration::Sample sample{}; + + // add sample to list + auto* sample_list = static_cast(*arg); + // Create a new element directly at the end of the vector + auto& sample = sample_list->push_back(); // prepare sample for decoding PrepareDecoding(pb_sample, sample); @@ -596,10 +601,6 @@ namespace // apply sample values AssignValues(pb_sample, sample); - // add sample to list - auto* sample_list = static_cast*>(*arg); - sample_list->push_back(sample); - return true; } @@ -615,7 +616,7 @@ namespace // prepare sample for decoding /////////////////////////////////////////////// pb_sample_list.samples.funcs.decode = &decode_sample_list_field; // NOLINT(*-pro-type-union-access) - pb_sample_list.samples.arg = ®istration_list_.samples; + pb_sample_list.samples.arg = ®istration_list_; /////////////////////////////////////////////// // decode it diff --git a/ecal/core/src/serialization/ecal_struct_sample_common.h b/ecal/core/src/serialization/ecal_struct_sample_common.h index 3524f24d7e..9a78d6121c 100644 --- a/ecal/core/src/serialization/ecal_struct_sample_common.h +++ b/ecal/core/src/serialization/ecal_struct_sample_common.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/ecal/core/src/serialization/ecal_struct_sample_registration.h b/ecal/core/src/serialization/ecal_struct_sample_registration.h index 0d0418aebf..cc9d133adc 100644 --- a/ecal/core/src/serialization/ecal_struct_sample_registration.h +++ b/ecal/core/src/serialization/ecal_struct_sample_registration.h @@ -28,6 +28,7 @@ #include "ecal_struct_service.h" #include +#include "util/expanding_vector.h" #include #include @@ -74,6 +75,11 @@ namespace eCAL bool operator==(const OSInfo& other) const { return osname == other.osname; } + + void clear() + { + osname.clear(); + } }; // eCAL host @@ -85,6 +91,12 @@ namespace eCAL bool operator==(const Host& other) const { return hname == other.hname && os == other.os; } + + void clear() + { + hname.clear(); + os.clear(); + } }; // Process severity information @@ -97,6 +109,13 @@ namespace eCAL bool operator==(const ProcessState& other) const { return severity == other.severity && severity_level == other.severity_level && info == other.info; } + + void clear() + { + severity = proc_sev_unknown; + severity_level = proc_sev_level_unknown; + info.clear(); + } }; // Transport layer parameters for ecal udp multicast @@ -106,6 +125,9 @@ namespace eCAL // Assuming there are no member variables to compare return true; } + + void clear() + {} }; // Transport layer parameters for ecal tcp @@ -117,16 +139,25 @@ namespace eCAL return port == other.port; } + void clear() + { + port = 0; + } }; // Transport layer parameters for ecal shm struct LayerParShm { - std::list memory_file_list; // list of memory file names + Util::CExpandingVector memory_file_list; // list of memory file names bool operator==(const LayerParShm& other) const { return memory_file_list == other.memory_file_list; } + + void clear() + { + memory_file_list.clear(); + } }; // Connection parameter for reader/writer @@ -141,6 +172,13 @@ namespace eCAL layer_par_tcp == other.layer_par_tcp && layer_par_shm == other.layer_par_shm; } + + void clear() + { + layer_par_udpmc.clear(); + layer_par_tcp.clear(); + layer_par_shm.clear(); + } }; // Transport layer information @@ -159,6 +197,15 @@ namespace eCAL active == other.active && par_layer == other.par_layer; } + + void clear() + { + type = tl_none; + version = 0; + enabled = false; + active = false; + par_layer.clear(); + } }; // Process information @@ -189,6 +236,21 @@ namespace eCAL component_init_info == other.component_init_info && ecal_runtime_version == other.ecal_runtime_version; } + + void clear() + { + rclock = 0; + hgname.clear(); + pname.clear(); + uname.clear(); + pparam.clear(); + state.clear(); + tsync_state = tsync_none; + tsync_mod_name.clear(); + component_init_state = 0; + component_init_info.clear(); + ecal_runtime_version.clear(); + } }; // eCAL topic information @@ -202,7 +264,7 @@ namespace eCAL std::string direction; // direction (publisher, subscriber) SDataTypeInformation tdatatype; // topic datatype information (encoding & type & description) - std::vector tlayer; // active topic transport layers and its specific parameter + Util::CExpandingVector tlayer; // active topic transport layers and its specific parameter int32_t tsize = 0; // topic size int32_t connections_loc = 0; // number of local connected entities @@ -233,6 +295,30 @@ namespace eCAL dfreq == other.dfreq && attr == other.attr; } + + void clear() + { + rclock = 0; + hgname.clear(); + pname.clear(); + uname.clear(); + tname.clear(); + direction.clear(); + tdatatype.clear(); + + tlayer.clear(); + tsize = 0; + + connections_loc = 0; + connections_ext = 0; + message_drops = 0; + + did = 0; + dclock = 0; + dfreq = 0; + + attr.clear(); + } }; struct SampleIdentifier @@ -250,6 +336,13 @@ namespace eCAL { return entity_id < other.entity_id; } + + void clear() + { + entity_id.clear(); + process_id = 0; + host_name.clear(); + } }; // Registration sample @@ -272,12 +365,20 @@ namespace eCAL client == other.client && topic == other.topic; } + + void clear() + { + identifier.clear(); + cmd_type = bct_none; + host.clear(); + process.clear(); + service.clear(); + client.clear(); + topic.clear(); + } }; // Registration sample list - struct SampleList - { - std::list samples; // list of Samples used currently by SHM registration - }; + using SampleList = Util::CExpandingVector; } } diff --git a/ecal/core/src/serialization/ecal_struct_service.h b/ecal/core/src/serialization/ecal_struct_service.h index 470f029845..9757207292 100644 --- a/ecal/core/src/serialization/ecal_struct_service.h +++ b/ecal/core/src/serialization/ecal_struct_service.h @@ -29,6 +29,8 @@ #include #include +#include + namespace eCAL { namespace Service @@ -61,6 +63,17 @@ namespace eCAL id == other.id && state == other.state; } + + void clear() + { + hname.clear(); + sname.clear(); + sid.clear(); + mname.clear(); + error.clear(); + id = 0; + state = none; + } }; // Service Request @@ -73,6 +86,12 @@ namespace eCAL return header == other.header && request == other.request; } + + void clear() + { + header.clear(); + request.clear(); + } }; // Service Response @@ -87,6 +106,13 @@ namespace eCAL response == other.response && ret_state == other.ret_state; } + + void clear() + { + header.clear(); + response.clear(); + ret_state = 0; + } }; // Service Method @@ -107,19 +133,29 @@ namespace eCAL resp_desc == other.resp_desc && call_count == other.call_count; } + + void clear() + { + mname.clear(); + req_type.clear(); + req_desc.clear(); + resp_type.clear(); + resp_desc.clear(); + call_count = 0; + } }; // Service struct Service { - int32_t rclock = 0; // Registration clock - std::string pname; // Process name - std::string uname; // Unit name - std::string sname; // Service name - std::vector methods; // List of methods - uint32_t version = 0; // Service protocol version - uint32_t tcp_port_v0 = 0; // The TCP port used for that service (v0) - uint32_t tcp_port_v1 = 0; // The TCP port used for that service (v1) + int32_t rclock = 0; // Registration clock + std::string pname; // Process name + std::string uname; // Unit name + std::string sname; // Service name + Util::CExpandingVector methods; // List of methods + uint32_t version = 0; // Service protocol version + uint32_t tcp_port_v0 = 0; // The TCP port used for that service (v0) + uint32_t tcp_port_v1 = 0; // The TCP port used for that service (v1) bool operator==(const Service& other) const { return rclock == other.rclock && @@ -131,17 +167,29 @@ namespace eCAL tcp_port_v0 == other.tcp_port_v0 && tcp_port_v1 == other.tcp_port_v1; } + + void clear() + { + rclock = 0; + pname.clear(); + uname.clear(); + sname.clear(); + methods.clear(); + version = 0; + tcp_port_v0 = 0; + tcp_port_v1 = 0; + } }; // Client struct Client { - int32_t rclock = 0; // Registration clock - std::string pname; // Process name - std::string uname; // Unit name - std::string sname; // Service name - std::vector methods; // List of methods - uint32_t version = 0; // Client protocol version + int32_t rclock = 0; // Registration clock + std::string pname; // Process name + std::string uname; // Unit name + std::string sname; // Service name + Util::CExpandingVector methods; // List of methods + uint32_t version = 0; // Client protocol version bool operator==(const Client& other) const { return rclock == other.rclock && @@ -151,6 +199,16 @@ namespace eCAL methods == other.methods && version == other.version; } + + void clear() + { + rclock = 0; + pname.clear(); + uname.clear(); + sname.clear(); + methods.clear(); + version = 0; + } }; } } diff --git a/ecal/core/src/service/ecal_clientgate.cpp b/ecal/core/src/service/ecal_clientgate.cpp index f72ca78c44..9f76498d3a 100644 --- a/ecal/core/src/service/ecal_clientgate.cpp +++ b/ecal/core/src/service/ecal_clientgate.cpp @@ -165,7 +165,7 @@ namespace eCAL std::shared_lock const lock(m_client_set_sync); for (const auto& service_client_impl : m_client_set) { - reg_sample_list_.samples.emplace_back(service_client_impl->GetRegistration()); + reg_sample_list_.push_back(service_client_impl->GetRegistration()); } } } diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index 1b275a5d50..8967b68442 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -102,7 +102,7 @@ namespace eCAL std::shared_lock const lock(m_service_set_sync); for (const auto& service_server_impl : m_service_set) { - reg_sample_list_.samples.emplace_back(service_server_impl->GetRegistration()); + reg_sample_list_.push_back(service_server_impl->GetRegistration()); } } } diff --git a/ecal/core/src/util/expanding_vector.h b/ecal/core/src/util/expanding_vector.h new file mode 100644 index 0000000000..e0077917cb --- /dev/null +++ b/ecal/core/src/util/expanding_vector.h @@ -0,0 +1,232 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief std::vector wrapper, that never deallocates element, just clears them +**/ + +#pragma once + +#include +#include +#include + +namespace eCAL +{ + namespace Util + { + /** + * @brief A vector that never destructs its elements, but only calls clear on them. + * + * @tparam T The type of the values. Must provide a `.clear()` function + * + * This class is *not* threadsafe and needs to be protected by locks / mutexes in multithreaded environments. + * + * From the outside / for the user, this class acts as a regular std::vector. + * However, when calling clear(), a regular vector will destroy the elements which are stored in this vector. + * This class, will instead call the `clear()` functions on all members. + */ + + // Templated class CExpandingVector + template + class CExpandingVector { + public: + using value_type = typename std::vector::value_type; + using allocator_type = typename std::vector::allocator_type; + using size_type = typename std::vector::size_type; + using difference_type = typename std::vector::difference_type; + using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; + using pointer = typename std::vector::pointer; + using const_pointer = typename std::vector::const_pointer; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + using reverse_iterator = typename std::vector::reverse_iterator; + using const_reverse_iterator = typename std::vector::const_reverse_iterator; + + private: + std::vector data; + size_t internal_size{ 0 }; // Track size separately + + public: + // Access to the internal size + size_t size() const { + return internal_size; + } + + // Clear the content but not the underlying vector + void clear() { + for (auto& elem : data) { + elem.clear(); // Call the clear() function on individual elements + } + // We don't modify the size of the underlying vector but keep the internal size consistent. + internal_size = 0; + } + + // Add new element to the vector + void push_back(const T& value) { + if (internal_size < data.size()) { + data[internal_size] = value; // Reuse space + } else { + data.push_back(value); // Expand the vector if needed + } + ++internal_size; + } + + // Push back (rvalue reference version for move semantics) + void push_back(T&& value) { + if (internal_size < data.size()) { + data[internal_size] = std::move(value); // Reuse space + } + else { + data.push_back(std::move(value)); // Move into the vector + } + ++internal_size; + } + + T& push_back() + { + if (internal_size == data.size()) { + data.push_back(T{}); // Expand the vector if needed, else do nothing + } + ++internal_size; + + return back(); + } + + // Access the first element + T& front() { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data.front(); // Return the first element + } + + const T& front() const { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data.front(); // Return the first element + } + + // Access the last element + T& back() { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data[internal_size - 1]; // Return the last element + } + + const T& back() const { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data[internal_size - 1]; // Return the last element + } + + + // Resize the vector + void resize(size_t new_size) { + if (new_size > data.size()) { + data.resize(new_size); + } + internal_size = new_size; + } + + // Access element with bounds checking + T& at(size_t index) { + if (index >= internal_size) { + throw std::out_of_range("Index out of bounds"); + } + return data.at(index); + } + + // Const access element with bounds checking + const T& at(size_t index) const { + if (index >= internal_size) { + throw std::out_of_range("Index out of bounds"); + } + return data.at(index); + } + + // Iterator functions + iterator begin() { + return data.begin(); + } + + const_iterator begin() const { + return data.begin(); + } + + iterator end() { + return data.begin() + internal_size; + } + + const_iterator end() const { + return data.begin() + internal_size; + } + + // Access the underlying capacity + size_t capacity() const { + return data.capacity(); + } + + // Operator[] overload + T& operator[](size_t index) { + return data[index]; + } + + const T& operator[](size_t index) const { + return data[index]; + } + + // Check if vector is empty + bool empty() const { + return internal_size == 0; + } + + // Get underlying vector's full size (not just the internal size) + size_t full_size() const { + return data.size(); + } + + // Equality operator (compares size and elements) + bool operator==(const CExpandingVector& other) const { + // Check if sizes are equal + if (internal_size != other.internal_size) { + return false; + } + + // Compare elements + for (size_t i = 0; i < internal_size; ++i) { + if (!(data[i] == other.data[i])) { + return false; + } + } + + return true; // Sizes are equal and all elements are the same + } + + bool operator!=(const CExpandingVector& other) const { + return !(*this == other); // Use the equality operator to implement inequality + } + }; + + } +} diff --git a/ecal/tests/cpp/serialization_test/src/registration_compare.cpp b/ecal/tests/cpp/serialization_test/src/registration_compare.cpp index 39682f868e..bc1c4f2040 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_compare.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_compare.cpp @@ -49,27 +49,6 @@ namespace eCAL (process1.ecal_runtime_version == process2.ecal_runtime_version); } - // compare two Method vector objects - bool CompareMethods(const std::vector& method1_vec, const std::vector& method2_vec) - { - // ensure that both vectors have the same size - if (method1_vec.size() != method2_vec.size()) { - return false; - } - - // compare vectors element-wise - return std::equal(method1_vec.begin(), method1_vec.end(), method2_vec.begin(), - [](const Service::Method& method1, const Service::Method& method2) { - // compare Method objects for equality - return (method1.mname == method2.mname) && - (method1.req_type == method2.req_type) && - (method1.req_desc == method2.req_desc) && - (method1.resp_type == method2.resp_type) && - (method1.resp_desc == method2.resp_desc) && - (method1.call_count == method2.call_count); - }); - } - // compare two Service objects bool CompareService(const Service::Service& service1, const Service::Service& service2) { @@ -77,7 +56,7 @@ namespace eCAL (service1.pname == service2.pname) && (service1.uname == service2.uname) && (service1.sname == service2.sname) && - CompareMethods(service1.methods, service2.methods) && + (service1.methods == service2.methods) && (service1.version == service2.version) && (service1.tcp_port_v0 == service2.tcp_port_v0) && (service1.tcp_port_v1 == service2.tcp_port_v1); @@ -90,7 +69,7 @@ namespace eCAL (client1.pname == client2.pname) && (client1.uname == client2.uname) && (client1.sname == client2.sname) && - CompareMethods(client1.methods, client2.methods) && + (client1.methods == client2.methods) && (client1.version == client2.version); } @@ -127,7 +106,7 @@ namespace eCAL } // compare two TLayer vector objects - bool CompareTLayer(const std::vector& layer1_vec, const std::vector& layer2_vec) + bool CompareTLayer(const Util::CExpandingVector& layer1_vec, const Util::CExpandingVector& layer2_vec) { // ensure that both vectors have the same size if (layer1_vec.size() != layer2_vec.size()) { diff --git a/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp b/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp index 82c9c1bd38..0478247cf1 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp @@ -77,7 +77,7 @@ namespace eCAL SampleList sample_list_in; for (const auto& sample_in : samples) { - sample_list_in.samples.push_back(sample_in); + sample_list_in.push_back(sample_in); } std::string sample_buffer; @@ -86,8 +86,8 @@ namespace eCAL SampleList sample_list_out; EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); - EXPECT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); - EXPECT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); + EXPECT_TRUE(sample_list_in.size() == sample_list_out.size()); + EXPECT_TRUE(std::equal(sample_list_in.begin(), sample_list_in.end(), sample_list_out.begin(), CompareRegistrationSamples)); } TEST_F(core_cpp_registration_serialization, RegistrationList2Vector) @@ -95,7 +95,7 @@ namespace eCAL SampleList sample_list_in; for (const auto& sample_in : samples) { - sample_list_in.samples.push_back(sample_in); + sample_list_in.push_back(sample_in); } std::vector sample_buffer; @@ -104,8 +104,8 @@ namespace eCAL SampleList sample_list_out; EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); - EXPECT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); - EXPECT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); + EXPECT_TRUE(sample_list_in.size() == sample_list_out.size()); + EXPECT_TRUE(std::equal(sample_list_in.begin(), sample_list_in.end(), sample_list_out.begin(), CompareRegistrationSamples)); } } } diff --git a/ecal/tests/cpp/util_test/CMakeLists.txt b/ecal/tests/cpp/util_test/CMakeLists.txt index d4e8b901b9..1f3f6d76ae 100644 --- a/ecal/tests/cpp/util_test/CMakeLists.txt +++ b/ecal/tests/cpp/util_test/CMakeLists.txt @@ -23,6 +23,7 @@ find_package(GTest REQUIRED) set(util_test_src src/util_test.cpp + src/expanding_vector_test.cpp ) ecal_add_gtest(${PROJECT_NAME} ${util_test_src}) diff --git a/ecal/tests/cpp/util_test/src/expanding_vector_test.cpp b/ecal/tests/cpp/util_test/src/expanding_vector_test.cpp new file mode 100644 index 0000000000..4e47541729 --- /dev/null +++ b/ecal/tests/cpp/util_test/src/expanding_vector_test.cpp @@ -0,0 +1,182 @@ +#include +#include "util/expanding_vector.h" // Assuming the class is defined in this header file + +using namespace eCAL::Util; + +// Define a simple class that has a clear function +class MyElement { +public: + int value; + + MyElement(int v = 0) : value(v) {} + + void clear() { + value = 0; // Reset the value to zero when clear is called + } + + bool operator==(const MyElement& other) const { + return value == other.value; + } + + friend std::ostream& operator<<(std::ostream& os, const MyElement& elem) { + os << elem.value; + return os; + } +}; + +// Unit test class for CExpandingVector +class CExpandingVectorTest : public ::testing::Test { +protected: + CExpandingVector vec; // Create a vector of MyElement type + + void SetUp() override { + // Add some initial values + vec.push_back(MyElement(1)); + vec.push_back(MyElement(2)); + vec.push_back(MyElement(3)); + } +}; + +using core_cpp_util_expanding_vector = CExpandingVectorTest; + +// Test push_back and size functionality +TEST_F(core_cpp_util_expanding_vector, PushBackAndSize) { + EXPECT_EQ(vec.size(), 3); // Size should be 3 after pushing 3 elements + + vec.push_back(MyElement(4)); + EXPECT_EQ(vec.size(), 4); // Size should now be 4 + + EXPECT_EQ(vec[0].value, 1); + EXPECT_EQ(vec[1].value, 2); + EXPECT_EQ(vec[2].value, 3); + EXPECT_EQ(vec[3].value, 4); +} + +// Test clear functionality +TEST_F(core_cpp_util_expanding_vector, ClearElements) { + vec.clear(); // Call clear, which should reset individual elements + + EXPECT_EQ(vec.size(), 0); // Internal size should be reset to 0 + + // Ensure underlying data is still there and reset to 0 + EXPECT_EQ(vec.full_size(), 3); // Underlying storage is still 3 + EXPECT_EQ(vec[0].value, 0); + EXPECT_EQ(vec[1].value, 0); + EXPECT_EQ(vec[2].value, 0); + + EXPECT_EQ(vec.push_back().value, 0); + EXPECT_EQ(vec.push_back().value, 0); +} + +// Test resize functionality +TEST_F(core_cpp_util_expanding_vector, ResizeVector) { + vec.resize(5); + EXPECT_EQ(vec.size(), 5); // Size should be 5 after resizing + + vec[3].value = 99; + vec[4].value = 100; + + EXPECT_EQ(vec[3].value, 99); + EXPECT_EQ(vec[4].value, 100); + + vec.resize(2); // Shrink the vector + EXPECT_EQ(vec.size(), 2); // Size should be 2 now + EXPECT_THROW(vec.at(3), std::out_of_range); // Accessing out of range should throw exception +} + +// Test operator[] without bounds checking +TEST_F(core_cpp_util_expanding_vector, OperatorAccess) { + EXPECT_NO_THROW(vec[1]); // Access valid index without throwing exception + EXPECT_EQ(vec[1].value, 2); // Element at index 1 should have value 2 + + vec[1].value = 42; + EXPECT_EQ(vec[1].value, 42); // After modification, value should be 42 + + // No bounds checking in operator[], so accessing beyond size is allowed + // but it's testing undefined behavior in C++ +} + +// Test at() function with bounds checking +TEST_F(core_cpp_util_expanding_vector, AtFunction) { + EXPECT_NO_THROW(vec.at(0)); + EXPECT_EQ(vec.at(0).value, 1); + + EXPECT_THROW(vec.at(10), std::out_of_range); // Out of bounds access should throw +} + +// Test begin() and end() iterator functions +TEST_F(core_cpp_util_expanding_vector, Iterators) { + auto it = vec.begin(); + EXPECT_EQ(it->value, 1); // First element should have value 1 + ++it; + EXPECT_EQ(it->value, 2); // Second element should have value 2 + + size_t count = 0; + for (auto it2 = vec.begin(); it2 != vec.end(); ++it2) { + ++count; + } + EXPECT_EQ(count, vec.size()); // Iterator loop should iterate over all valid elements +} + +// Test const_iterator functionality +TEST_F(core_cpp_util_expanding_vector, ConstIterator) { + const CExpandingVector constVec = vec; + + CExpandingVector::const_iterator it = constVec.begin(); + EXPECT_EQ(it->value, 1); // First element in const vector should be 1 + ++it; + EXPECT_EQ(it->value, 2); // Second element in const vector should be 2 + + size_t count = 0; + for (auto it2 = constVec.begin(); it2 != constVec.end(); ++it2) { + ++count; + } + EXPECT_EQ(count, constVec.size()); // Iterator loop should iterate over all valid elements +} + +// Test empty() function +TEST_F(core_cpp_util_expanding_vector, EmptyFunction) { + EXPECT_FALSE(vec.empty()); // Should not be empty initially + + vec.clear(); + EXPECT_TRUE(vec.empty()); // Should be empty after clear is called +} + +// Test capacity and full_size functions +TEST_F(core_cpp_util_expanding_vector, FullSize) { + EXPECT_EQ(vec.full_size(), 3); // Full size is the size of the underlying vector + + vec.push_back(MyElement(4)); + EXPECT_EQ(vec.full_size(), 4); // Full size is the new capacity + + vec.clear(); + EXPECT_EQ(vec.full_size(), 4); // Full size is the new capacity +} + + +TEST_F(core_cpp_util_expanding_vector, Front) { + EXPECT_EQ(vec.front().value, 1); // The first element should have value 1 + + vec.front().value = 10; // Modify the first element + EXPECT_EQ(vec.front().value, 10); // Check if the modification worked +} + +// Test back function +TEST_F(core_cpp_util_expanding_vector, Back) { + EXPECT_EQ(vec.back().value, 3); // The last element should have value 3 + + vec.back().value = 20; // Modify the last element + EXPECT_EQ(vec.back().value, 20); // Check if the modification worked + + vec.clear(); + vec.push_back(10); // push back a new value + EXPECT_EQ(vec.back().value, 10); // Check if the modification worked +} + +// Test front and back with an empty vector (should throw an exception) +TEST_F(core_cpp_util_expanding_vector, FrontAndBackEmptyVector) { + CExpandingVector emptyVec; + + EXPECT_THROW(emptyVec.front(), std::out_of_range); // Should throw because vector is empty + EXPECT_THROW(emptyVec.back(), std::out_of_range); // Should throw because vector is empty +}