From 1c22db65ccb2fabacfdbc89c5209f48decce43df Mon Sep 17 00:00:00 2001 From: methylDragon Date: Wed, 15 Mar 2023 20:41:59 -0700 Subject: [PATCH 01/11] Add type description utils Signed-off-by: methylDragon --- rosidl_runtime_c/CMakeLists.txt | 6 + .../rosidl_runtime_c/type_description_utils.h | 654 ++++++++ rosidl_runtime_c/src/type_description_utils.c | 1396 +++++++++++++++++ .../test/test_type_description_utils.cpp | 558 +++++++ 4 files changed, 2614 insertions(+) create mode 100644 rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h create mode 100644 rosidl_runtime_c/src/type_description_utils.c create mode 100644 rosidl_runtime_c/test/test_type_description_utils.cpp diff --git a/rosidl_runtime_c/CMakeLists.txt b/rosidl_runtime_c/CMakeLists.txt index a3cbbbd36..74351ae75 100644 --- a/rosidl_runtime_c/CMakeLists.txt +++ b/rosidl_runtime_c/CMakeLists.txt @@ -25,6 +25,7 @@ add_library(${PROJECT_NAME} "src/string_functions.c" "src/type_hash.c" "src/u16string_functions.c" + "src/type_description_utils.c" ${type_description_sources} ) target_include_directories(${PROJECT_NAME} PUBLIC @@ -136,6 +137,11 @@ if(BUILD_TESTING) target_compile_definitions(test_u16string_functions PUBLIC RCUTILS_ENABLE_FAULT_INJECTION) endif() + ament_add_gtest(test_type_description_utils test/test_type_description_utils.cpp) + if(TARGET test_type_description_utils) + target_link_libraries(test_type_description_utils ${PROJECT_NAME}) + endif() + add_performance_test(benchmark_string_conversion test/benchmark/benchmark_string_conversion.cpp) if(TARGET benchmark_string_conversion) target_link_libraries(benchmark_string_conversion ${PROJECT_NAME}) diff --git a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h new file mode 100644 index 000000000..3358a9363 --- /dev/null +++ b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h @@ -0,0 +1,654 @@ +// Copyright 2023 Open Source Robotics Foundation, Inc. +// +// 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. + +/** rosidl_runtime_c_type_description_utils: Utilities for manipulating type_description_interfaces + * C message structs + */ + +// NOTE(methylDragon): I made it so that every instance of a non-message struct (e.g. hash map) +// borrows, whereas the message structs copy. +// So lifetime should be managed by the message structs. + +#ifndef ROSIDL_RUNTIME_C__TYPE_DESCRIPTION_UTILS_H_ +#define ROSIDL_RUNTIME_C__TYPE_DESCRIPTION_UTILS_H_ + +#include +#include +#include + +#include + +#include +#include + +#include "rosidl_runtime_c/type_description/field__functions.h" +#include "rosidl_runtime_c/type_description/field__struct.h" +#include "rosidl_runtime_c/type_description/individual_type_description__functions.h" +#include "rosidl_runtime_c/type_description/individual_type_description__struct.h" +#include "rosidl_runtime_c/type_description/type_description__functions.h" +#include "rosidl_runtime_c/type_description/type_description__struct.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + +static const uint8_t ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER = 48; + + +// ================================================================================================= +// GET BY NAME +// ================================================================================================= + +/// Get the first Field, matching by name. +/** + * NOTE: The `field` output arg must be passed in pointing to `NULL`. + * It will remain pointing to `NULL` if no matching `Field` is found. + * + * Ownership: + * - The output `Field` borrows the `fields` arg's `Field` element. It is not authorized to delete + * its value and cannot outlive the owner it borrows from. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] fields array of fields to search through + * \param[in] name field name of the field to look for + * \param[out] field the first field with field name that matches the name arg. + * Must point to `NULL`, outputs pointing to `NULL` if not found. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_NOT_FOUND if no `Field` with a matching name is found, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_find_field( + const rosidl_runtime_c__type_description__Field__Sequence * fields, + const char * name, + rosidl_runtime_c__type_description__Field ** field); + +/// Get the first referenced IndividualTypeDescription, matching by type name. +/** + * NOTE: The `referenced_type` output arg must be passed in pointing `NULL`. + * It will remain pointing to `NULL` if no matching `IndividualTypeDescription` is found. + * + * Ownership: + * - The output `IndividualTypeDescription` borrows the `referenced_types` arg's + * `IndividualTypeDescription` element. It is not authorized to delete its value and cannot + * outlive the `referenced_types` it borrows from. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] referenced_types array of referenced individual type descriptions to search through + * \param[in] type_name name of the referenced referenced individual type description to look for + * \param[out] referenced_type the first individual type description with type name that matches the + * type_name arg. Must point to `NULL`, outputs pointing to `NULL` if + * not found. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_NOT_FOUND if no `IndividualTypeDescription` with matching name is found, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_find_referenced_type_description( + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types, + const char * type_name, + rosidl_runtime_c__type_description__IndividualTypeDescription ** referenced_type); + + +// ================================================================================================= +// HASH MAPS +// ================================================================================================= + +/// Construct a hash map of an `IndividualTypeDescription`'s `Field` objects, keyed by field name. +/** + * NOTE: The `hash_map` output arg must be passed in pointing to `NULL`. + * + * Ownership: + * - The caller assumes ownership of the output `rcutils_hash_map_t` and must free it and its + * elements, but NOT finalize its values. + * - The output `rcutils_hash_map_t` has values that borrows the `individual_description` arg's + * `Field` objects. It is not authorized to delete its values and it cannot outlive the owner it + * borrows from. + * - Finalizing the `rcutils_hash_map_t` should not result in the map's values getting finalized. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] individual_description the individual type description to get the fields from + * \param[in] allocator the allocator to use through out the lifetime of the hash_map + * \param[out] hash_map `rcutils_hash_map_t` to be initialized, containing `Field` objects keyed by + * their field names. Must point to `NULL`, outputs pointing to `NULL` + * if error. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_field_map( + const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_description, + const rcutils_allocator_t * allocator, + rcutils_hash_map_t ** hash_map); + +/// Construct a hash map of an `IndividualTypeDescription__Sequence`'s `IndividualTypeDescription` +/// objects, keyed by type name. +/** + * NOTE: The `hash_map` output arg must be passed in pointing to `NULL`. + * Furthermore, if the input `referenced_types` sequence has types with identical names but + * differing structures, this function will return `RCUTILS_RET_INVALID_ARGUMENT` and fail. + * + * Ownership: + * - The caller assumes ownership of the output `rcutils_hash_map_t` and must free it and its + * elements, but NOT finalize its values. + * - The output `rcutils_hash_map_t` has values that borrows the `referenced_types` arg's + * `IndividualTypeDescription` elements. It is not authorized to delete its values and it cannot + * outlive the `referenced_types` it borrows from. + * - Finalizing the output `rcutils_hash_map_t` should not result in its values getting finalized. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] referenced_types the referenced individual type descriptions to get the referenced + * types from + * \param[in] allocator the allocator to use through out the lifetime of the hash_map + * \param[out] hash_map `rcutils_hash_map_t` to be initialized, containing + * `IndividualTypeDescription` objects keyed by their type names. Must point to + * `NULL`, outputs pointing to `NULL` if error. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types, + const rcutils_allocator_t * allocator, + rcutils_hash_map_t ** hash_map); + + +// ================================================================================================= +// DESCRIPTION VALIDITY +// ================================================================================================= + +/// Return a map of only the referenced type descriptions that are recursively necessary. +/** + * NOTE: The `seen_map` output arg must be passed in pointing to `NULL`. It's a parameter so it can + * be passed into subsequent recursive calls to traverse nested types. + * + * A referenced type description is recursively necessary if it is either: + * - Needed by a field of the main IndividualTypeDescription + * - Needed by a field of any prior necessary referenced type descriptions (hence recursive) + * + * For more clarity, an unnecessary referenced type description will not be + * referenced when parsing a TypeDescription, and hence can be excluded. + * + * Ownership: + * - The caller assumes ownership of the output `rcutils_hash_map_t` and must free it and its + * elements, but NOT finalize its values. + * - The output `rcutils_hash_map_t` has values that borrows the `referenced_types_map` arg's + * `IndividualTypeDescription` values. It is not authorized to delete its values and it cannot + * outlive owner it borrows from. + * - Finalizing the output `rcutils_hash_map_t` should not result in its values getting finalized. + * + * Procedure: + * 1. Create seen map + * 2. [Iterate through fields]: + * 3. If field is not nested type or field's nested type is seen in the seen map: + * - Continue + * 4. If nested type is missing in referenced types or nested type name is empty: + * - Throw error + * 5. Else: + * - Add to seen map + * - Recurse on referenced type + * 6. Output seen map + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] main_type_description the main individual type description to check the fields of + * \param[in] referenced_types_map a map of referenced `IndividualTypeDescription` objects from the + * main individual type description's parent `TypeDescription` + * object, keyed by their type names + * \param[in] allocator the allocator to use through out the lifetime of the hash_map + * \param[in,out] seen_map `rcutils_hash_map_t` of seen necessary `IndividualTypeDescription` + * objects keyed by their type names. Used in recursive calls. Must point to + * `NULL` for user's top level call, outputs pointing to `NULL` if error. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_NOT_FOUND if passed referenced types are missing necessary types, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( + const rosidl_runtime_c__type_description__IndividualTypeDescription * main_type_description, + const rcutils_hash_map_t * referenced_types_map, + const rcutils_allocator_t * allocator, + rcutils_hash_map_t ** seen_map); + +/// Deep copy a map of individual type descriptions into a new IndividualTypeDescription__Sequence. +/** + * NOTE: The `sequence` output arg must be passed in pointing to `NULL`. + * + * This method also validates that each IndividualTypeDescription in the map has a type name that + * matches the key it was indexed by. + * + * Ownership: + * - The caller assumes ownership of the output `IndividualTypeDescription__Sequence` and must free + * it. + * - The output `IndividualTypeDescription__Sequence` deep copies the values from the `hash_map` arg + * and so has a separate lifetime from the `hash_map`. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] hash_map the referenced type descriptions map to convert (via deep copy) to a sequence + * \param[out] sequence the `IndividualTypeDescription__Sequence` containing copies of the + * `IndividualTypeDescription` objects stored in the values of the input + * `hash_map` arg. Must point to `NULL`, outputs pointing to `NULL` if error. + * \param[in] sort sorts the referenced type descriptions if true, best effort + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_descriptions_map( + const rcutils_hash_map_t * hash_map, + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence ** sequence, + bool sort); + +/// Helper comparison function for the sorting function +ROSIDL_GENERATOR_C_PUBLIC +int +rosidl_runtime_c_type_description_utils_referenced_type_description_sequence_sort_compare( + const void * lhs, const void * rhs); + +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * sequence); + +/// Remove unnecessary referenced type descriptions from a sequence of referenced types. +/// IndividualTypeDescription elements are COPY ASSIGNED in-place, and the original sequence is +/// shrunken afterwards. +/// NOTE: DOES NOT SORT AFTER PRUNING! Call sort separately. +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_place( + const rosidl_runtime_c__type_description__IndividualTypeDescription * main_type_description, + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types); + +/// Check if field is valid +/** + * A field is valid if: + * - Its name is not empty + * - Note this does not check for valid chars/double underscores + * - Its field type: + * - Is set + * - Has a non-empty nested type name if nested + */ +ROSIDL_GENERATOR_C_PUBLIC +bool +rosidl_runtime_c_type_description_utils_field_is_valid( + rosidl_runtime_c__type_description__Field * field); + +/// Check if individual type description is valid +/** + * An individual type description is valid if: + * - Its type name is not empty + * - Note this does not check for valid chars etc. + * - All of its fields are valid + * - It does not have duplicate fields + */ +ROSIDL_GENERATOR_C_PUBLIC +bool +rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + rosidl_runtime_c__type_description__IndividualTypeDescription * description); + +/// Check if type description is valid +/** + * An type description is valid if: + * - Its main individual type description is valid + * - Its referenced type descriptions are: + * - Not duplicated + * - Not missing necessary type descriptions + * - Have no unnecessary type descriptions + * - Individually valid + * - Sorted + */ +ROSIDL_GENERATOR_C_PUBLIC +bool +rosidl_runtime_c_type_description_utils_type_description_is_valid( + rosidl_runtime_c__type_description__TypeDescription * description); + +/// This is on a best effort basis, it won't work if the fields of the main or necessary referenced +/// types are invalid. It prunes then sorts. +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( + rosidl_runtime_c__type_description__TypeDescription * type_description); + + +// ================================================================================================= +// DESCRIPTION CONSTRUCTION +// ================================================================================================= + +/// Construct a new populated `Field` object. +/** + * NOTE: The `field` output arg must be passed in pointing to `NULL`. + * + * Ownership: + * - The caller assumes ownership of the output `Field` and must free it and its members. + * - The char * members are copied. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] name the name of the field + * \param[in] name_length the string length of `name` (not counting the null terminator) + * \param[in] type_id the type id of the field (follows the type_description_interfaces definition) + * \param[in] capacity capacity of the field (if it is an array or sequence) + * \param[in] string_capacity capacity of string elements (if the field is an array or sequence of + * strings) + * \param[in] nested_type_name the nested type name of the field (if it is a nested type) + * \param[in] nested_type_name_length the string length of `nested_type_name` (not counting the null + * terminator) + * \param[in] default_value literal default value of the field as a string, as it appeared in the + * original message definition + * \param[in] default_value_length the string length of `default_value` (not counting the null + * terminator) + * \param[out] field `Field` to be initialized. Must point to `NULL`, outputs pointing to `NULL` if + * error. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_create_field( + const char * name, size_t name_length, + uint8_t type_id, uint64_t capacity, uint64_t string_capacity, + const char * nested_type_name, size_t nested_type_name_length, + const char * default_value, size_t default_value_length, + rosidl_runtime_c__type_description__Field ** field); + +/// Construct a new populated `IndividualTypeDescription` object, with fields sequence of size 0. +/** + * NOTE: The `individual_description` output arg must be passed in pointing to `NULL`. + * + * Ownership: + * - The caller assumes ownership of the output `IndividualTypeDescription` and must free it and its + * members. + * - The char * members are copied. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] type_name the name of the individual type description + * \param[in] type_name_length the string length of `type_name` (not counting the null terminator) + * \param[out] individual_description `IndividualTypeDescription` to be initialized. Must point to + * `NULL`, outputs pointing to `NULL` if error. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_create_individual_type_description( + const char * type_name, size_t type_name_length, + rosidl_runtime_c__type_description__IndividualTypeDescription ** individual_description); + +/// Construct a new populated `TypeDescription` object, with fields and referenced types sequences +/// of size 0. +/** + * NOTE: The `description` output arg must be passed in pointing to `NULL`. + * + * Ownership: + * - The caller assumes ownership of the output `TypeDescription` and must free it and its + * members. + * - The char * members are copied. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in] type_name the name of the type description's main individual type + * \param[in] type_name_length the string length of `type_name` (not counting the null terminator) + * \param[out] type_description `TypeDescription` to be initialized. Must point to `NULL`, + * outputs pointing to `NULL` if error. + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_create_type_description( + const char * type_name, size_t type_name_length, + rosidl_runtime_c__type_description__TypeDescription ** type_description); + +/// Append a `Field` to an `IndividualTypeDescription`, extending the sequence. +/** + * + * Ownership: + * - A deep-copy of the `field` is appended to `individual_type_description` on success, the + * `individual_type_description` is then responsible for freeing the copied `field`. + * - The ownership of the input `field` is not transferred, it must still be freed. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in,out] individual_type_description the individual type description to append to, noop on + * failure + * \param[in,out] field the field to append + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_append_field( + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, + rosidl_runtime_c__type_description__Field * field); + +/// Append a referenced `IndividualTypeDescription` to a `TypeDescription`, extending it. +/** + * + * Ownership: + * - A deep-copy of the `referenced_type_description` is appended to `type_description` on success, + * the `type_description` is then responsible for freeing the copied + * `referenced_type_description`. + * - The ownership of the input `referenced_type_description` is not transferred, it must still be + * freed. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in,out] type_description the type description to append to, noop on failure + * \param[in,out] individual_type_description the individual type description to append + * \param[in] sort sorts the referenced type descriptions if true, best effort + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + rosidl_runtime_c__type_description__TypeDescription * type_description, + rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_type_description, + bool sort); + +/// Append a referenced `TypeDescription` to a `TypeDescription`, extending it with recursive types. +/** + * + * Ownership: + * - A deep-copy of the `type_description_to_append` object's main individual type description is + * appended to `type_description` on success, the `type_description` is then responsible for + * freeing the copied individual type descriptions. + * - Deep-copies of the `type_description_to_append` object's referenced individual type + * descriptions are appended to `type_description` on success, the `type_description` is then + * responsible for freeing the copied individual type descriptions (or the remaining ones if + * they are pruned). + * - The ownership of the input `type_description` is not transferred, it must still be + * freed. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * + * \param[in,out] type_description the type description to append to, noop on failure + * \param[in,out] type_description the type description to append + * \param[in] coerce_to_valid coerces input type_description to valid before output if true + * (pruning and sorting), best effort + * \return #RCUTILS_RET_OK if successful, or + * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or + * \return #RCUTILS_RET_ERROR if an unknown error occurs. + */ +// This adds the type description's main description as a referenced type, and all necessary +// referenced types. Then it prunes and sorts the resulting referenced types sequence. +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_append_referenced_type_description( + rosidl_runtime_c__type_description__TypeDescription * type_description, + rosidl_runtime_c__type_description__TypeDescription * type_description_to_append, + bool coerce_to_valid); + +// Copy main type description, then validate if coerce_to_valid is true +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_descriptions, + rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, + rosidl_runtime_c__type_description__TypeDescription ** output_description, + bool coerce_to_valid); + + +// ================================================================================================= +// DESCRIPTION PRINTING +// ================================================================================================= + +/// Print rosidl_runtime_c__type_description__FieldType +ROSIDL_GENERATOR_C_PUBLIC +void +rosidl_runtime_c_type_description_utils_print_field_type( + const rosidl_runtime_c__type_description__FieldType * field_type); + +/// Print rosidl_runtime_c__type_description__Field +ROSIDL_GENERATOR_C_PUBLIC +void +rosidl_runtime_c_type_description_utils_print_field( + const rosidl_runtime_c__type_description__Field * field); + +/// Print rosidl_runtime_c__type_description__IndividualTypeDescription +ROSIDL_GENERATOR_C_PUBLIC +void +rosidl_runtime_c_type_description_utils_print_individual_type_description( + const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description +); + +/// Print rosidl_runtime_c__type_description__TypeDescription +ROSIDL_GENERATOR_C_PUBLIC +void rosidl_runtime_c_type_description_utils_print_type_description( + const rosidl_runtime_c__type_description__TypeDescription * type_description); + +/// Print hashmap of rosidl_runtime_c__type_description__Field, keyed by name +ROSIDL_GENERATOR_C_PUBLIC +void +rosidl_runtime_c_type_description_utils_print_field_map(const rcutils_hash_map_t * hash_map); + +/// Print hashmap of rosidl_runtime_c__type_description__IndividualTypeDescription, keyed by name +ROSIDL_GENERATOR_C_PUBLIC +void +rosidl_runtime_c_type_description_utils_print_referenced_type_description_map( + const rcutils_hash_map_t * hash_map); + + +#ifdef __cplusplus +} +#endif + +#endif // ROSIDL_RUNTIME_C__TYPE_DESCRIPTION_UTILS_H_ diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c new file mode 100644 index 000000000..5af5d5658 --- /dev/null +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -0,0 +1,1396 @@ +// Copyright 2023 Open Source Robotics Foundation, Inc. +// +// 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. + +// NOTE(methylDragon): Maybe this belongs inside the type_description_interfaces +// package instead? +// But as a C header (.h)??? + +// NOTE(methylDragon): I made it so that every instance of a non-message struct (e.g. hash map) +// borrows, whereas the message structs copy. +// So lifetime should be managed by the message structs. + +#include "rosidl_runtime_c/type_description_utils.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "rosidl_runtime_c/type_description/field__functions.h" +#include "rosidl_runtime_c/type_description/field__struct.h" +#include "rosidl_runtime_c/type_description/individual_type_description__functions.h" +#include "rosidl_runtime_c/type_description/individual_type_description__struct.h" +#include "rosidl_runtime_c/type_description/type_description__functions.h" +#include "rosidl_runtime_c/type_description/type_description__struct.h" + + +// Modified from https://stackoverflow.com/questions/4398711/round-to-the-nearest-power-of-two +int next_power_of_two(int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; // next power of 2 + + return v > 1 ? v : 1; +} + +// ================================================================================================= +// GET BY NAME +// ================================================================================================= + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_find_field( + const rosidl_runtime_c__type_description__Field__Sequence * fields, + const char * name, + rosidl_runtime_c__type_description__Field ** field) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(fields, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(name, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(field, RCUTILS_RET_INVALID_ARGUMENT); + if (*field != NULL) { + RCUTILS_LOG_ERROR("`field` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + for (size_t i = 0; i < fields->size; i++) { + if (strcmp(fields->data[i].name.data, name) == 0) { + *field = &fields->data[i]; + return RCUTILS_RET_OK; + } + } + + RCUTILS_LOG_WARN("Could not find field: %s", name); + return RCUTILS_RET_NOT_FOUND; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_find_referenced_type_description( + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types, + const char * type_name, + rosidl_runtime_c__type_description__IndividualTypeDescription ** referenced_type) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_types, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_name, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_type, RCUTILS_RET_INVALID_ARGUMENT); + if (*referenced_type != NULL) { + RCUTILS_LOG_ERROR("`referenced_type` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + for (size_t i = 0; i < referenced_types->size; i++) { + if (strcmp(referenced_types->data[i].type_name.data, type_name) == 0) { + *referenced_type = &referenced_types->data[i]; + return RCUTILS_RET_OK; + } + } + + RCUTILS_LOG_WARN("Could not find referenced type description: %s", type_name); + return RCUTILS_RET_NOT_FOUND; +} + + +// ================================================================================================= +// HASH MAPS +// ================================================================================================= + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_field_map( + const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_description, + const rcutils_allocator_t * allocator, + rcutils_hash_map_t ** hash_map) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(individual_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(allocator, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(hash_map, RCUTILS_RET_INVALID_ARGUMENT); + if (*hash_map != NULL) { + RCUTILS_LOG_ERROR("`hash_map` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + rcutils_hash_map_t * out = allocator->allocate(sizeof(rcutils_hash_map_t), allocator->state); + if (out == NULL) { + RCUTILS_LOG_ERROR("Could not allocate output hash map"); + return RCUTILS_RET_BAD_ALLOC; + } + *out = rcutils_get_zero_initialized_hash_map(); + + rcutils_ret_t ret = RCUTILS_RET_ERROR; + rcutils_ret_t fail_ret = RCUTILS_RET_ERROR; + + ret = rcutils_hash_map_init( + out, next_power_of_two(individual_description->fields.size), + sizeof(char *), sizeof(rosidl_runtime_c__type_description__Field *), + rcutils_hash_map_string_hash_func, rcutils_hash_map_string_cmp_func, + allocator); + if (ret != RCUTILS_RET_OK) { + allocator->deallocate(out, allocator->state); + RCUTILS_LOG_ERROR("Could not init hash map"); + return ret; + } + + for (size_t i = 0; i < individual_description->fields.size; i++) { + rosidl_runtime_c__type_description__Field * tmp = &individual_description->fields.data[i]; + // Passing tmp is fine even if tmp goes out of scope later since it copies in the set method... + ret = rcutils_hash_map_set(out, &individual_description->fields.data[i].name.data, &tmp); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR( + "Could not set hash map entry for field: %s", + individual_description->fields.data[i].name.data); + fail_ret = ret; + goto fail; + } + } + + *hash_map = out; + return RCUTILS_RET_OK; + +fail: + { + rcutils_ret_t fini_ret = rcutils_hash_map_fini(out); + if (fini_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize hash map"); + } + allocator->deallocate(out, allocator->state); + } + return fail_ret; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types, + const rcutils_allocator_t * allocator, + rcutils_hash_map_t ** hash_map) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_types, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(allocator, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(hash_map, RCUTILS_RET_INVALID_ARGUMENT); + if (*hash_map != NULL) { + RCUTILS_LOG_ERROR("`hash_map` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + rcutils_hash_map_t * out = allocator->allocate(sizeof(rcutils_hash_map_t), allocator->state); + if (out == NULL) { + RCUTILS_LOG_ERROR("Could not allocate output hash map"); + return RCUTILS_RET_BAD_ALLOC; + } + *out = rcutils_get_zero_initialized_hash_map(); + + rcutils_ret_t ret = RCUTILS_RET_ERROR; + rcutils_ret_t fail_ret = RCUTILS_RET_ERROR; + + ret = rcutils_hash_map_init( + out, next_power_of_two(referenced_types->size), + sizeof(char *), sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription *), + rcutils_hash_map_string_hash_func, rcutils_hash_map_string_cmp_func, + allocator); + if (ret != RCUTILS_RET_OK) { + allocator->deallocate(out, allocator->state); + RCUTILS_LOG_ERROR("Could not init hash map"); + return ret; + } + + for (size_t i = 0; i < referenced_types->size; i++) { + rosidl_runtime_c__type_description__IndividualTypeDescription * tmp = + &referenced_types->data[i]; + + // Check for duplicate referenced types + if (rcutils_hash_map_key_exists(out, &referenced_types->data[i].type_name.data)) { + rosidl_runtime_c__type_description__IndividualTypeDescription * stored_description; + ret = rcutils_hash_map_get( + out, &referenced_types->data[i].type_name.data, &stored_description); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR( + "Could not get stored description: %s", referenced_types->data[i].type_name.data); + fail_ret = ret; // Most likely a RCUTILS_RET_NOT_FOUND + goto fail; + } + + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &referenced_types->data[i], stored_description)) + { + // Non-identical duplicate referenced types is invalid (it's ambiguous which one to use) + RCUTILS_LOG_ERROR( + "Passed referenced IndividualTypeDescriptions has non-identical duplicate types"); + fail_ret = RCUTILS_RET_INVALID_ARGUMENT; + goto fail; + } + } + + // Passing tmp is fine even if tmp goes out of scope later since it copies in the set method... + ret = rcutils_hash_map_set(out, &referenced_types->data[i].type_name.data, &tmp); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR( + "Could not set hash map entry for referenced type: %s", + referenced_types->data[i].type_name.data); + fail_ret = ret; + goto fail; + } + } + + size_t map_length; + ret = rcutils_hash_map_get_size(out, &map_length); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not get size of hash map for validation"); + fail_ret = RCUTILS_RET_ERROR; + goto fail; + } + + *hash_map = out; + return RCUTILS_RET_OK; + +fail: + { + rcutils_ret_t fini_ret = rcutils_hash_map_fini(out); + if (fini_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize hash map"); + } + allocator->deallocate(out, allocator->state); + } + return fail_ret; +} + + +// ================================================================================================= +// DESCRIPTION VALIDITY +// ================================================================================================= + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( + const rosidl_runtime_c__type_description__IndividualTypeDescription * main_type_description, + const rcutils_hash_map_t * referenced_types_map, + const rcutils_allocator_t * allocator, + rcutils_hash_map_t ** seen_map) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(main_type_description, RCUTILS_RET_INVALID_ARGUMENT); + HASH_MAP_VALIDATE_HASH_MAP(referenced_types_map); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(allocator, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(seen_map, RCUTILS_RET_INVALID_ARGUMENT); + + // Only true for the top level call, so we can determine when to finalize the map + bool top_level_call = false; + rcutils_ret_t ret = RCUTILS_RET_ERROR; + rcutils_ret_t fail_ret = RCUTILS_RET_ERROR; + + // 1. Init new hash map only on the top level call + if (!*seen_map) { + top_level_call = true; + + *seen_map = allocator->allocate(sizeof(rcutils_hash_map_t), allocator->state); + if (*seen_map == NULL) { + RCUTILS_LOG_ERROR("Could not allocate hash map"); + return RCUTILS_RET_BAD_ALLOC; + } + **seen_map = rcutils_get_zero_initialized_hash_map(); + + size_t referenced_types_map_size; + ret = rcutils_hash_map_get_size(referenced_types_map, &referenced_types_map_size); + if (ret != RCUTILS_RET_OK) { + allocator->deallocate(*seen_map, allocator->state); + RCUTILS_LOG_ERROR("Could not get size of referenced types hash map"); + *seen_map = NULL; + return RCUTILS_RET_ERROR; + } + + ret = rcutils_hash_map_init( + *seen_map, next_power_of_two(referenced_types_map_size), + sizeof(char *), sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription *), + rcutils_hash_map_string_hash_func, rcutils_hash_map_string_cmp_func, + allocator); + if (ret != RCUTILS_RET_OK) { + allocator->deallocate(*seen_map, allocator->state); + RCUTILS_LOG_ERROR("Could not init hash map"); + *seen_map = NULL; + return RCUTILS_RET_BAD_ALLOC; + } + } + + // 2. Iterate through fields + for (size_t i = 0; i < main_type_description->fields.size; i++) { + rosidl_runtime_c__type_description__Field * field = &main_type_description->fields.data[i]; + // 3. Skip cases + // continue if field is not nested type or nested type is in seen map: + if ((field->type.type_id % + ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER) != 1 || + rcutils_hash_map_key_exists(*seen_map, &field->type.nested_type_name.data)) + { + continue; + } + + // 4. Error cases + // Referenced type does not exist + if (!rcutils_hash_map_key_exists(referenced_types_map, &field->type.nested_type_name.data)) { + RCUTILS_LOG_ERROR("Missing referenced type: %s", field->type.nested_type_name.data); + fail_ret = RCUTILS_RET_NOT_FOUND; + goto fail; + } + // Nested name empty + if (field->type.nested_type_name.size == 0) { + RCUTILS_LOG_ERROR("Missing referenced type name"); + fail_ret = RCUTILS_RET_INVALID_ARGUMENT; + goto fail; + } + + // 5. Add to seen map (we didn't skip and didn't error out) + rosidl_runtime_c__type_description__IndividualTypeDescription * necessary_description; + + ret = rcutils_hash_map_get( + referenced_types_map, &field->type.nested_type_name.data, &necessary_description); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR( + "Could not get necessary referenced type: %s", field->type.nested_type_name.data); + fail_ret = ret; // Most likely a RCUTILS_RET_NOT_FOUND + goto fail; + } + + // Check for mismatched name + if (strcmp(field->type.nested_type_name.data, necessary_description->type_name.data) != 0) { + RCUTILS_LOG_ERROR( + "Necessary referenced type name (%s) did not match field's nested type name (%s)", + necessary_description->type_name.data, + field->type.nested_type_name.data); + fail_ret = RCUTILS_RET_INVALID_ARGUMENT; + goto fail; + } + + // Add to map (finally!!) + ret = rcutils_hash_map_set( + *seen_map, &field->type.nested_type_name.data, &necessary_description); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to set hash map for key: %s", field->type.nested_type_name.data); + fail_ret = ret; + goto fail; + } + + // Recurse on fields on necessary_description + ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( + necessary_description, referenced_types_map, allocator, seen_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Recursion failed on: %s", necessary_description->type_name.data); + fail_ret = ret; + goto fail; + } + } // End field iteration + + return RCUTILS_RET_OK; + +fail: + if (top_level_call) { + rcutils_ret_t fini_ret = rcutils_hash_map_fini(*seen_map); + if (fini_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize hash map"); + } + allocator->deallocate(*seen_map, allocator->state); + *seen_map = NULL; + } + return fail_ret; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_descriptions_map( + const rcutils_hash_map_t * hash_map, + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence ** sequence, + bool sort) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(hash_map, RCUTILS_RET_INVALID_ARGUMENT); + if (*sequence != NULL) { + RCUTILS_LOG_ERROR("`sequence` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + size_t map_length; + rcutils_ret_t ret = RCUTILS_RET_ERROR; + ret = rcutils_hash_map_get_size(hash_map, &map_length); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not get size of hash map"); + return RCUTILS_RET_ERROR; + } + *sequence = rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__create( + map_length); + if (*sequence == NULL) { + RCUTILS_LOG_ERROR("Could allocate sequence"); + return RCUTILS_RET_BAD_ALLOC; + } + + size_t i = 0; + char * key; + rosidl_runtime_c__type_description__IndividualTypeDescription * data; + rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); + while (RCUTILS_RET_OK == status) { + if (strcmp(key, data->type_name.data) != 0) { + RCUTILS_LOG_ERROR( + "Necessary referenced type name (%s) did not match key (%s)", data->type_name.data, key); + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__destroy(*sequence); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + // Deep copy + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( + data, &((*sequence)->data[i]))) + { + RCUTILS_LOG_ERROR("Could not copy type %s to sequence", key); + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__destroy(*sequence); + return RCUTILS_RET_BAD_ALLOC; + } + + i += 1; + status = rcutils_hash_map_get_next_key_and_data(hash_map, &key, &key, &data); + } + + if (sort) { + rcutils_ret_t ret = + rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place(*sequence); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_WARN("Could not sort copy of referenced type descriptions for validation"); + } + } + + return RCUTILS_RET_OK; +} + + +int +rosidl_runtime_c_type_description_utils_referenced_type_description_sequence_sort_compare( + const void * lhs, const void * rhs) +{ + rosidl_runtime_c__type_description__IndividualTypeDescription * left = + (rosidl_runtime_c__type_description__IndividualTypeDescription *)lhs; + rosidl_runtime_c__type_description__IndividualTypeDescription * right = + (rosidl_runtime_c__type_description__IndividualTypeDescription *)rhs; + if (left == NULL) { + return right == NULL ? 0 : 1; + } else if (right == NULL) { + return -1; + } + return strcmp(left->type_name.data, right->type_name.data); +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * sequence) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(sequence, RCUTILS_RET_INVALID_ARGUMENT); + return rcutils_qsort( + sequence->data, + sequence->size, + sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription), + rosidl_runtime_c_type_description_utils_referenced_type_description_sequence_sort_compare); +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_place( + const rosidl_runtime_c__type_description__IndividualTypeDescription * main_type_description, + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(main_type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_types, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_ret_t ret = RCUTILS_RET_ERROR; + rcutils_ret_t end_ret = RCUTILS_RET_ERROR; + rcutils_ret_t fini_ret = RCUTILS_RET_ERROR; + + rcutils_hash_map_t * referenced_types_map = NULL; + rcutils_hash_map_t * necessary_map = NULL; + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + ret = rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( + referenced_types, &allocator, &referenced_types_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not construct referenced type description map"); + return ret; + } + + ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( + main_type_description, referenced_types_map, &allocator, &necessary_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not construct necessary referenced type description map"); + end_ret = ret; + goto end_ref; + } + + size_t map_length; + ret = rcutils_hash_map_get_size(necessary_map, &map_length); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not get size of hash map for validation"); + end_ret = RCUTILS_RET_ERROR; + goto end_necessary; + } + // End early if pruning was not needed + if (referenced_types->size == map_length) { + end_ret = RCUTILS_RET_OK; + goto end_necessary; + } + + size_t append_count = 0; + char * key; + rosidl_runtime_c__type_description__IndividualTypeDescription * data = NULL; + rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(necessary_map, NULL, &key, &data); + while (RCUTILS_RET_OK == status) { + if (strcmp(key, data->type_name.data) != 0) { + RCUTILS_LOG_ERROR( + "Necessary referenced type name (%s) did not match key (%s)", data->type_name.data, key); + end_ret = RCUTILS_RET_ERROR; + goto end_necessary; + } + + // Deep copy if necessary + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + data, &referenced_types->data[append_count])) + { + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( + data, &referenced_types->data[append_count++])) + { + RCUTILS_LOG_ERROR( + "Could not copy necessary referenced type description %s to rearrange", key); + end_ret = RCUTILS_RET_ERROR; + goto end_necessary; + } + } else { + append_count++; + } + status = rcutils_hash_map_get_next_key_and_data(necessary_map, &key, &key, &data); + } + + // Finalize entries after the section of necessary referenced types, and shrink the input sequence + for (size_t i = append_count; i < referenced_types->size; i++) { + rosidl_runtime_c__type_description__IndividualTypeDescription__fini( + &referenced_types->data[i]); + } + size_t allocation_size = + append_count * sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription); + + rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = + allocator.reallocate(referenced_types->data, allocation_size, allocator.state); + if (next_ptr == NULL) { + RCUTILS_LOG_ERROR( + "Could not shrink the necessary referenced type descriptions sequence during rearrangement! " + "Beware! The referenced type descriptions was likely already partially modified in place!"); + end_ret = RCUTILS_RET_BAD_ALLOC; + goto end_necessary; + } + referenced_types->data = next_ptr; + referenced_types->size = append_count; + referenced_types->capacity = append_count; + + end_ret = RCUTILS_RET_OK; + +end_necessary: + { + fini_ret = rcutils_hash_map_fini(necessary_map); + if (fini_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize hash map"); + } + allocator.deallocate(necessary_map, allocator.state); + } + +end_ref: + { + fini_ret = rcutils_hash_map_fini(referenced_types_map); + if (fini_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize hash map"); + } + allocator.deallocate(referenced_types_map, allocator.state); + } + return end_ret; +} + + +bool +rosidl_runtime_c_type_description_utils_field_is_valid( + rosidl_runtime_c__type_description__Field * field) +{ + if (field == NULL) { + RCUTILS_LOG_WARN("Field is invalid: Pointer is null"); + return false; + } + if (field->name.size == 0) { + RCUTILS_LOG_WARN("Field is invalid: Empty name"); + return false; + } + if ((field->type.type_id % ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER) == + 0) + { + RCUTILS_LOG_WARN("Field `%s` is invalid: Unset type ID", field->name.data); + return false; + } + if ((field->type.type_id % ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER) == + 1 && field->type.nested_type_name.size == 0) + { + RCUTILS_LOG_WARN( + "Field `%s` is invalid: Field is nested but with empty nested type name", field->name.data); + return false; + } + return true; +} + + +bool +rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + rosidl_runtime_c__type_description__IndividualTypeDescription * description) +{ + if (description == NULL) { + RCUTILS_LOG_WARN("Individual type description is invalid: Pointer is null"); + return false; + } + if (description->type_name.size == 0) { + RCUTILS_LOG_WARN("Individual type description is invalid: Empty name"); + return false; + } + + for (size_t i = 0; i < description->fields.size; i++) { + if (!rosidl_runtime_c_type_description_utils_field_is_valid(&description->fields.data[i])) { + RCUTILS_LOG_WARN( + "Individual type description `%s` is invalid: Invalid field", description->type_name.data); + return false; + } + } + + bool end_ret = false; + rcutils_hash_map_t * hash_map = NULL; + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_get_field_map( + description, &allocator, &hash_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not construct field map for validation"); + return false; + } + + size_t map_length; + ret = rcutils_hash_map_get_size(hash_map, &map_length); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not get size of field map for validation"); + goto end; + } + + if (description->fields.size != map_length) { + RCUTILS_LOG_WARN( + "Individual type description `%s` is invalid: Duplicate fields", description->type_name.data); + goto end; + } + + end_ret = true; + +end: + { + rcutils_ret_t fini_ret = rcutils_hash_map_fini(hash_map); + if (fini_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize hash map"); + } + allocator.deallocate(hash_map, allocator.state); + return end_ret; + } +} + + +bool +rosidl_runtime_c_type_description_utils_type_description_is_valid( + rosidl_runtime_c__type_description__TypeDescription * description) +{ + if (description == NULL) { + RCUTILS_LOG_WARN("Type description is invalid: Pointer is null"); + return false; + } + + if (!rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + &description->type_description)) + { + if (description->type_description.type_name.size != 0) { + RCUTILS_LOG_WARN( + "Type description `%s` is invalid: Main type description is invalid", + description->type_description.type_name.data); + } else { + RCUTILS_LOG_WARN("Type description is invalid: Main type description is invalid"); + } + return false; + } + + bool end_ret = false; + rcutils_hash_map_t * referenced_types_map = NULL; + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( + &description->referenced_type_descriptions, &allocator, &referenced_types_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not construct referenced type description map"); + return false; + } + + size_t map_length; + + // Check no duplicated ref types + ret = rcutils_hash_map_get_size(referenced_types_map, &map_length); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not get size of referenced type description map for validation"); + goto end_ref; + } + if (description->referenced_type_descriptions.size != map_length) { + RCUTILS_LOG_WARN( + "Type description `%s` is invalid: Duplicate referenced type descriptions", + description->type_description.type_name.data); + goto end_ref; + } + + // Check no missing necessary ref types + rcutils_hash_map_t * necessary_types_map = NULL; + ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( + &description->type_description, referenced_types_map, &allocator, &necessary_types_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not construct necessary referenced type description map"); + goto end_ref; + } + + // Check no unnecessary ref types + ret = rcutils_hash_map_get_size(necessary_types_map, &map_length); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR( + "Could not get size of necessary referenced type description map for validation"); + goto end_necessary; + } + + if (description->referenced_type_descriptions.size != map_length) { + RCUTILS_LOG_WARN( + "Type description `%s` is invalid: Unnecessary referenced type descriptions", + description->type_description.type_name.data); + goto end_necessary; + } + + // Check all referenced types valid (the prior checks ensure all of them are necessary) + for (size_t i = 0; i < description->referenced_type_descriptions.size; i++) { + if (!rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + &description->referenced_type_descriptions.data[i])) + { + RCUTILS_LOG_WARN( + "Type description `%s` is invalid: Invalid referenced type description", + description->type_description.type_name.data); + goto end_necessary; + } + } + + // Check referenced types sorted + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * sorted_sequence = + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__create(map_length); + if (sorted_sequence == NULL) { + RCUTILS_LOG_ERROR("Could allocate sequence for copy of referenced type descriptions"); + goto end_necessary; + } + if (rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__copy( + &description->referenced_type_descriptions, sorted_sequence)) + { + RCUTILS_LOG_ERROR("Could not copy referenced type descriptions for validation"); + goto end_sequence; + } + ret = rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( + sorted_sequence); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not sort copy of referenced type descriptions for validation"); + goto end_sequence; + } + + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__are_equal( + &description->referenced_type_descriptions, sorted_sequence)) + { + RCUTILS_LOG_WARN( + "Type description `%s` is invalid: Referenced type descriptions not sorted", + description->type_description.type_name.data); + goto end_sequence; + } + + end_ret = true; + +end_sequence: + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__destroy(sorted_sequence); + +end_necessary: + ret = rcutils_hash_map_fini(necessary_types_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize referenced types map"); + } + allocator.deallocate(necessary_types_map, allocator.state); + +end_ref: + ret = rcutils_hash_map_fini(referenced_types_map); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Failed to finalize referenced types map"); + } + allocator.deallocate(referenced_types_map, allocator.state); + + return end_ret; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( + rosidl_runtime_c__type_description__TypeDescription * type_description) +{ + if (!rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + &type_description->type_description)) + { + RCUTILS_LOG_ERROR("Could not make type description valid: Invalid main type description"); + return RCUTILS_RET_ERROR; + } + + rcutils_ret_t ret; + ret = rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_place( + &type_description->type_description, &type_description->referenced_type_descriptions); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_WARN( + "Could not make type description valid: Could not prune referenced_type_descriptions"); + return ret; + } + + ret = rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( + &type_description->referenced_type_descriptions); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_WARN( + "Could not make type description valid: Could not sort referenced_type_descriptions"); + return ret; + } + + return RCUTILS_RET_OK; +} + + +// ================================================================================================= +// DESCRIPTION CONSTRUCTION +// ================================================================================================= + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_create_field( + const char * name, size_t name_length, + uint8_t type_id, uint64_t capacity, uint64_t string_capacity, + const char * nested_type_name, size_t nested_type_name_length, + const char * default_value, size_t default_value_length, + rosidl_runtime_c__type_description__Field ** field) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(name, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(nested_type_name, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(default_value, RCUTILS_RET_INVALID_ARGUMENT); + if (*field != NULL) { + RCUTILS_LOG_ERROR("`field` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + *field = rosidl_runtime_c__type_description__Field__create(); + + // Field + if (!rosidl_runtime_c__String__assignn(&(*field)->name, name, name_length)) { + RCUTILS_LOG_ERROR("Could not assign field name"); + rosidl_runtime_c__type_description__Field__destroy(*field); + *field = NULL; + return RCUTILS_RET_BAD_ALLOC; + } + if (!rosidl_runtime_c__String__assignn( + &(*field)->default_value, default_value, default_value_length)) + { + RCUTILS_LOG_ERROR("Could not assign field default value"); + rosidl_runtime_c__type_description__Field__destroy(*field); + *field = NULL; + return RCUTILS_RET_BAD_ALLOC; + } + + // FieldType + (*field)->type.type_id = type_id; + (*field)->type.capacity = capacity; + (*field)->type.string_capacity = string_capacity; + + if (!rosidl_runtime_c__String__assignn( + &(*field)->type.nested_type_name, nested_type_name, nested_type_name_length)) + { + RCUTILS_LOG_ERROR("Could not assign field nested type name"); + rosidl_runtime_c__type_description__Field__destroy(*field); + *field = NULL; + return RCUTILS_RET_BAD_ALLOC; + } + + return RCUTILS_RET_OK; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_create_individual_type_description( + const char * type_name, size_t type_name_length, + rosidl_runtime_c__type_description__IndividualTypeDescription ** individual_description) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_name, RCUTILS_RET_INVALID_ARGUMENT); + if (*individual_description != NULL) { + RCUTILS_LOG_ERROR("`individual_description` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + *individual_description = rosidl_runtime_c__type_description__IndividualTypeDescription__create(); + + if (!rosidl_runtime_c__String__assignn( + &(*individual_description)->type_name, type_name, type_name_length)) + { + RCUTILS_LOG_ERROR("Could not assign individual description type name"); + rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(*individual_description); + *individual_description = NULL; + return RCUTILS_RET_BAD_ALLOC; + } + return RCUTILS_RET_OK; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_create_type_description( + const char * type_name, size_t type_name_length, + rosidl_runtime_c__type_description__TypeDescription ** type_description) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_name, RCUTILS_RET_INVALID_ARGUMENT); + if (*type_description != NULL) { + RCUTILS_LOG_ERROR("`type_description` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + *type_description = rosidl_runtime_c__type_description__TypeDescription__create(); + + if (!rosidl_runtime_c__String__assignn( + &(*type_description)->type_description.type_name, type_name, type_name_length)) + { + RCUTILS_LOG_ERROR("Could not assign main individual description type name"); + rosidl_runtime_c__type_description__TypeDescription__destroy(*type_description); + *type_description = NULL; + return RCUTILS_RET_BAD_ALLOC; + } + return RCUTILS_RET_OK; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_append_field( + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, + rosidl_runtime_c__type_description__Field * field) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(individual_type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(field, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + rcutils_ret_t fini_ret = RCUTILS_RET_ERROR; + + size_t allocation_size = ( + (individual_type_description->fields.size + 1) * + sizeof(rosidl_runtime_c__type_description__Field) + ); + size_t last_index = individual_type_description->fields.size; + rosidl_runtime_c__type_description__Field * next_ptr = allocator.reallocate( + individual_type_description->fields.data, allocation_size, allocator.state); + if (next_ptr == NULL) { + RCUTILS_LOG_ERROR("Could not realloc individual type description fields sequence"); + return RCUTILS_RET_BAD_ALLOC; + } + individual_type_description->fields.data = next_ptr; + individual_type_description->fields.size += 1; + individual_type_description->fields.capacity += 1; + + if (!rosidl_runtime_c__type_description__Field__init(&next_ptr[last_index])) { + RCUTILS_LOG_ERROR("Could not init new individual type description field element"); + fini_ret = RCUTILS_RET_BAD_ALLOC; + goto fail; + } + + if (!rosidl_runtime_c__type_description__Field__copy(field, &next_ptr[last_index])) { + RCUTILS_LOG_ERROR("Could not copy into new individual type description field element"); + rosidl_runtime_c__type_description__Field__fini(&next_ptr[last_index]); + fini_ret = RCUTILS_RET_ERROR; + goto fail; + } + + return RCUTILS_RET_OK; + +fail: + // Attempt to undo on failure + next_ptr = allocator.reallocate( + individual_type_description->fields.data, + individual_type_description->fields.size * sizeof(rosidl_runtime_c__type_description__Field), + allocator.state); + if (next_ptr == NULL) { + RCUTILS_LOG_ERROR( + "Could not shorten individual type description fields sequence. " + "Excess memory will be UNINITIALIZED!"); + } else { + individual_type_description->fields.data = next_ptr; + individual_type_description->fields.size -= 1; + individual_type_description->fields.capacity -= 1; + } + return fini_ret; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + rosidl_runtime_c__type_description__TypeDescription * type_description, + rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_type_description, + bool sort) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_type_description, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + rcutils_ret_t fini_ret = RCUTILS_RET_ERROR; + + size_t allocation_size = ( + (type_description->referenced_type_descriptions.size + 1) * + sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription) + ); + size_t last_index = type_description->referenced_type_descriptions.size; + + rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate( + type_description->referenced_type_descriptions.data, allocation_size, allocator.state); + if (next_ptr == NULL) { + RCUTILS_LOG_ERROR("Could not realloc type description referenced type descriptions sequence"); + return RCUTILS_RET_BAD_ALLOC; + } + type_description->referenced_type_descriptions.data = next_ptr; + type_description->referenced_type_descriptions.size += 1; + type_description->referenced_type_descriptions.capacity += 1; + + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__init(&next_ptr[last_index])) { + RCUTILS_LOG_ERROR("Could not init new type description referenced type descriptions element"); + fini_ret = RCUTILS_RET_BAD_ALLOC; + goto fail; + } + + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( + referenced_type_description, &next_ptr[last_index])) + { + // Attempt to undo changes on failure + RCUTILS_LOG_ERROR( + "Could not copy into new type description referenced type descriptions element"); + rosidl_runtime_c__type_description__IndividualTypeDescription__fini(&next_ptr[last_index]); + fini_ret = RCUTILS_RET_ERROR; + goto fail; + } + + if (sort) { + rcutils_ret_t ret = + rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( + &type_description->referenced_type_descriptions); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_WARN("Could not sort copy of referenced type descriptions for validation"); + } + } + return RCUTILS_RET_OK; + +fail: + // Attempt to undo on failure + next_ptr = allocator.reallocate( + type_description->referenced_type_descriptions.data, + ( + type_description->referenced_type_descriptions.size * + sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription) + ), + allocator.state); + if (next_ptr == NULL) { + RCUTILS_LOG_ERROR( + "Could not shorten type description referenced type descriptions sequence. " + "Excess memory will be UNINITIALIZED!"); + } else { + type_description->referenced_type_descriptions.data = next_ptr; + type_description->referenced_type_descriptions.size -= 1; + type_description->referenced_type_descriptions.capacity -= 1; + } + return fini_ret; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_append_referenced_type_description( + rosidl_runtime_c__type_description__TypeDescription * type_description, + rosidl_runtime_c__type_description__TypeDescription * type_description_to_append, + bool coerce_to_valid) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_description_to_append, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + rcutils_ret_t fini_ret = RCUTILS_RET_ERROR; + + // +1 for the type_description_to_append's main type description + size_t extend_count = type_description_to_append->referenced_type_descriptions.size + 1; + size_t allocation_size = ( + (type_description->referenced_type_descriptions.size + extend_count) * + sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription) + ); + rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate( + type_description->referenced_type_descriptions.data, allocation_size, allocator.state); + if (next_ptr == NULL) { + RCUTILS_LOG_ERROR("Could not realloc type description referenced type descriptions sequence"); + return RCUTILS_RET_BAD_ALLOC; + } + + size_t init_reset_size = 0; + size_t last_index = type_description->referenced_type_descriptions.size; + for (size_t i = last_index; i < last_index + extend_count; i++) { + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__init(&next_ptr[i])) { + RCUTILS_LOG_ERROR("Could not init new type description referenced type descriptions element"); + fini_ret = RCUTILS_RET_BAD_ALLOC; + goto fail; + } + init_reset_size += 1; + } + + // Copy type_description_to_append's main type description + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( + &type_description_to_append->type_description, &next_ptr[last_index])) + { + RCUTILS_LOG_ERROR( + "Could not copy into new type description referenced type descriptions element"); + fini_ret = RCUTILS_RET_ERROR; + goto fail; + } + + // Copy type_description_to_append's referenced type descriptions + // There are (extend_count - 1) referenced type descriptions to copy + for (size_t i = last_index + 1; i < last_index + extend_count; i++) { + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( + &type_description_to_append->referenced_type_descriptions.data[i - 1 - last_index], + &next_ptr[i])) + { + RCUTILS_LOG_ERROR("Could not copy new type description referenced type descriptions element"); + fini_ret = RCUTILS_RET_BAD_ALLOC; + goto fail; + } + init_reset_size += 1; + } + + type_description->referenced_type_descriptions.data = next_ptr; + type_description->referenced_type_descriptions.size += extend_count; + type_description->referenced_type_descriptions.capacity += extend_count; + + if (coerce_to_valid) { + rcutils_ret_t ret = + rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( + type_description); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_WARN("Could not coerce type description to valid!"); + return RCUTILS_RET_WARN; + } + } + + return RCUTILS_RET_OK; + +fail: + // Attempt to undo changes on failure + for (size_t j = last_index; j < last_index + init_reset_size; j++) { + rosidl_runtime_c__type_description__IndividualTypeDescription__fini(&next_ptr[j]); + } + next_ptr = allocator.reallocate( + type_description->referenced_type_descriptions.data, + ( + type_description->referenced_type_descriptions.size * + sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription) + ), + allocator.state); + if (next_ptr == NULL) { + RCUTILS_LOG_ERROR( + "Could not shorten type description referenced type descriptions sequence. " + "Excess memory will be UNINITIALIZED!"); + type_description->referenced_type_descriptions.size += extend_count; + type_description->referenced_type_descriptions.capacity += extend_count; + } + return fini_ret; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_descriptions, + rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, + rosidl_runtime_c__type_description__TypeDescription ** output_description, + bool coerce_to_valid) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_descriptions, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_description, RCUTILS_RET_INVALID_ARGUMENT); + if (*output_description != NULL) { + RCUTILS_LOG_ERROR("`output_description` output argument is not pointing to null"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + *output_description = rosidl_runtime_c__type_description__TypeDescription__create(); + if (*output_description == NULL) { + RCUTILS_LOG_ERROR("Could not create output type description"); + return RCUTILS_RET_BAD_ALLOC; + } + + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( + referenced_description, &(*output_description)->type_description)) + { + RCUTILS_LOG_ERROR("Could not copy referenced type description into main description"); + rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); + *output_description = NULL; + return RCUTILS_RET_ERROR; + } + + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__copy( + referenced_descriptions, &(*output_description)->referenced_type_descriptions)) + { + RCUTILS_LOG_ERROR("Could not copy referenced type descriptions"); + rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); + *output_description = NULL; + return RCUTILS_RET_ERROR; + } + + if (coerce_to_valid) { + rcutils_ret_t ret = + rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( + *output_description); + if (ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not coerce output type description to valid"); + rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); + *output_description = NULL; + return ret; + } + } + return RCUTILS_RET_OK; +} + + +// ================================================================================================= +// DESCRIPTION PRINTING +// ================================================================================================= + +void +rosidl_runtime_c_type_description_utils_print_field_type( + const rosidl_runtime_c__type_description__FieldType * field_type) +{ + printf( + " [FIELD TYPE]\n" + " type_id: %d\n" + " capacity: %ld\n" + " string_capacity: %ld\n", + field_type->type_id, field_type->capacity, field_type->string_capacity); + + if (field_type->nested_type_name.data == NULL) { + printf(" nested_type_name: %s\n", field_type->nested_type_name.data); + } else { + printf(" nested_type_name: \"%s\"\n", field_type->nested_type_name.data); + } +} + + +void +rosidl_runtime_c_type_description_utils_print_field( + const rosidl_runtime_c__type_description__Field * field) +{ + printf("[FIELD]\n"); + + if (field->name.data == NULL) { + printf(" name: %s\n", field->name.data); + } else { + printf(" name: \"%s\"\n", field->name.data); + } + + if (field->default_value.data == NULL) { + printf(" default_value: %s\n", field->default_value.data); + } else { + printf(" default_value: \"%s\"\n", field->default_value.data); + } + + rosidl_runtime_c_type_description_utils_print_field_type(&field->type); +} + + +void +rosidl_runtime_c_type_description_utils_print_individual_type_description( + const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description) +{ + printf( + "\n[INDIVIDUAL TYPE DESCRIPTION] (Fields: %ld)\n", individual_type_description->fields.size); + + if (individual_type_description->type_name.data == NULL) { + printf(" type_name: %s\n", individual_type_description->type_name.data); + } else { + printf(" type_name: \"%s\"\n", individual_type_description->type_name.data); + } + + for (size_t i = 0; i < individual_type_description->fields.size; i++) { + rosidl_runtime_c_type_description_utils_print_field( + &individual_type_description->fields.data[i]); + } +} + + +void rosidl_runtime_c_type_description_utils_print_type_description( + const rosidl_runtime_c__type_description__TypeDescription * type_description) +{ + printf("\n\n---\n\n"); + + printf( + "= [PRINTING TYPE DESCRIPTION] = (Referenced descriptions: %ld)\n", + type_description->referenced_type_descriptions.size); + + printf("\n== [MAIN DESCRIPTION] ==\n"); + rosidl_runtime_c_type_description_utils_print_individual_type_description( + &type_description->type_description); + + printf("\n== [REFERENCED DESCRIPTIONS] ==\n"); + for (size_t i = 0; i < type_description->referenced_type_descriptions.size; i++) { + rosidl_runtime_c_type_description_utils_print_individual_type_description( + &type_description->referenced_type_descriptions.data[i]); + } + + printf("\n---\n\n"); +} + + +void +rosidl_runtime_c_type_description_utils_print_field_map(const rcutils_hash_map_t * hash_map) +{ + char * key; + rosidl_runtime_c__type_description__Field * data = NULL; + rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); + while (RCUTILS_RET_OK == status) { + if (key == NULL) { + printf("\n== KEY: %s ==\n", key); + } else { + printf("\n== KEY: \"%s\" ==\n", key); + } + rosidl_runtime_c_type_description_utils_print_field(data); + status = rcutils_hash_map_get_next_key_and_data(hash_map, &key, &key, &data); + } +} + + +void +rosidl_runtime_c_type_description_utils_print_referenced_type_description_map( + const rcutils_hash_map_t * hash_map) +{ + char * key; + rosidl_runtime_c__type_description__IndividualTypeDescription * data; + rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); + while (RCUTILS_RET_OK == status) { + if (key == NULL) { + printf("\n== KEY: %s ==\n", key); + } else { + printf("\n== KEY: \"%s\" ==\n", key); + } + rosidl_runtime_c_type_description_utils_print_individual_type_description(data); + status = rcutils_hash_map_get_next_key_and_data(hash_map, &key, &key, &data); + } +} diff --git a/rosidl_runtime_c/test/test_type_description_utils.cpp b/rosidl_runtime_c/test/test_type_description_utils.cpp new file mode 100644 index 000000000..50e908f95 --- /dev/null +++ b/rosidl_runtime_c/test/test_type_description_utils.cpp @@ -0,0 +1,558 @@ +// Copyright 2023 Open Source Robotics Foundation, Inc. +// +// 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. + +#include "rosidl_runtime_c/type_description/field__functions.h" +#include "rosidl_runtime_c/type_description/field__struct.h" +#include "rosidl_runtime_c/type_description/individual_type_description__functions.h" +#include "rosidl_runtime_c/type_description/individual_type_description__struct.h" +#include "rosidl_runtime_c/type_description/type_description__functions.h" +#include "rosidl_runtime_c/type_description/type_description__struct.h" +#include "rosidl_runtime_c/type_description_utils.h" + +#include +#include +#include + +#include +#include + +#include + + +TEST(TestUtils, test_basic_construction) +{ + const std::string test_name = "test_name"; + + // TypeDescription + { + rosidl_runtime_c__type_description__TypeDescription * desc = + rosidl_runtime_c__type_description__TypeDescription__create(); + ASSERT_TRUE(desc); + ASSERT_TRUE( + rosidl_runtime_c__String__assignn( + &desc->type_description.type_name, test_name.c_str(), test_name.size())); + + rosidl_runtime_c__type_description__TypeDescription * utils_desc = NULL; + EXPECT_EQ( + rosidl_runtime_c_type_description_utils_create_type_description( + test_name.c_str(), + test_name.size(), &utils_desc), + RCUTILS_RET_OK); + ASSERT_TRUE(utils_desc); + + EXPECT_FALSE(utils_desc->type_description.fields.data); + EXPECT_EQ(utils_desc->type_description.fields.size, 0); + EXPECT_EQ(utils_desc->type_description.fields.capacity, 0); + + EXPECT_FALSE(utils_desc->referenced_type_descriptions.data); + EXPECT_EQ(utils_desc->referenced_type_descriptions.size, 0); + EXPECT_EQ(utils_desc->referenced_type_descriptions.capacity, 0); + + EXPECT_TRUE(rosidl_runtime_c__type_description__TypeDescription__are_equal(desc, utils_desc)); + + rosidl_runtime_c__type_description__TypeDescription__destroy(desc); + rosidl_runtime_c__type_description__TypeDescription__destroy(utils_desc); + } + + // IndividualTypeDescription + { + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_desc = + rosidl_runtime_c__type_description__IndividualTypeDescription__create(); + ASSERT_TRUE(individual_desc); + ASSERT_TRUE( + rosidl_runtime_c__String__assignn( + &individual_desc->type_name, test_name.c_str(), test_name.size())); + + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_utils_desc = NULL; + EXPECT_EQ( + rosidl_runtime_c_type_description_utils_create_individual_type_description( + test_name.c_str(), test_name.size(), &individual_utils_desc), + RCUTILS_RET_OK); + + ASSERT_TRUE(individual_utils_desc); + + EXPECT_FALSE(individual_utils_desc->fields.data); + EXPECT_EQ(individual_utils_desc->fields.size, 0); + EXPECT_EQ(individual_utils_desc->fields.capacity, 0); + + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + individual_desc, individual_utils_desc)); + + rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(individual_desc); + rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(individual_utils_desc); + } + + // Field + { + rosidl_runtime_c__type_description__Field * field = + rosidl_runtime_c__type_description__Field__create(); + ASSERT_TRUE(field); + ASSERT_TRUE( + rosidl_runtime_c__String__assignn( + &field->name, test_name.c_str(), test_name.size())); + + rosidl_runtime_c__type_description__Field * utils_field = NULL; + EXPECT_EQ( + rosidl_runtime_c_type_description_utils_create_field( + test_name.c_str(), test_name.size(), 0, // Name, Name Length, Type ID + 0, 0, // Capacity, String Capacity + "", 0, // Nested Type Name, Nested Type Name Length + "", 0, // Default Value, Default Value Length + &utils_field), + RCUTILS_RET_OK); + ASSERT_TRUE(utils_field); + + EXPECT_TRUE(utils_field->default_value.data); + + EXPECT_EQ(utils_field->default_value.size, 0); + EXPECT_EQ(utils_field->default_value.capacity, 1); // Null terminator + + EXPECT_TRUE(rosidl_runtime_c__type_description__Field__are_equal(field, utils_field)); + + rosidl_runtime_c__type_description__Field__destroy(field); + rosidl_runtime_c__type_description__Field__destroy(utils_field); + } +} + + +class TestUtilsFixture: public ::testing::Test +{ +public: + void SetUp() + { + type_description_1 = NULL; + rosidl_runtime_c_type_description_utils_create_type_description("t1", 2, &type_description_1); + ASSERT_TRUE(type_description_1); + + type_description_2 = NULL; + rosidl_runtime_c_type_description_utils_create_type_description("t2", 2, &type_description_2); + ASSERT_TRUE(type_description_2); + + type_description_3 = NULL; + rosidl_runtime_c_type_description_utils_create_type_description("t3", 2, &type_description_3); + ASSERT_TRUE(type_description_3); + + + individual_desc_1 = NULL; + rosidl_runtime_c_type_description_utils_create_individual_type_description( + "i1", 2, &individual_desc_1); + ASSERT_TRUE(individual_desc_1); + + individual_desc_2 = NULL; + rosidl_runtime_c_type_description_utils_create_individual_type_description( + "i2", 2, &individual_desc_2); + ASSERT_TRUE(individual_desc_2); + + individual_desc_3 = NULL; + rosidl_runtime_c_type_description_utils_create_individual_type_description( + "i3", 2, &individual_desc_3); + ASSERT_TRUE(individual_desc_3); + + empty_individual_desc = NULL; + rosidl_runtime_c_type_description_utils_create_individual_type_description( + "empty", 5, &empty_individual_desc); + ASSERT_TRUE(empty_individual_desc); + + + field_1 = NULL; // Nested + rosidl_runtime_c_type_description_utils_create_field( + "f1", 2, 1, // Name, Name Length, Type ID + 0, 0, // Capacity, String Capacity + "empty", 5, // Nested Type Name, Nested Type Name Length + "", 0, // Default Value, Default Value Length + &field_1); + ASSERT_TRUE(field_1); + + field_2 = NULL; + rosidl_runtime_c_type_description_utils_create_field( + "f2", 2, 2, // Name, Name Length, Type ID + 0, 0, "", 0, "", 0, &field_2); + ASSERT_TRUE(field_2); + + field_3 = NULL; + rosidl_runtime_c_type_description_utils_create_field( + "f3", 2, 3, // Name, Name Length, Type ID + 0, 0, "", 0, "", 0, &field_3); + ASSERT_TRUE(field_3); + } + + void TearDown() + { + rosidl_runtime_c__type_description__TypeDescription__destroy(type_description_1); + rosidl_runtime_c__type_description__TypeDescription__destroy(type_description_2); + rosidl_runtime_c__type_description__TypeDescription__destroy(type_description_3); + rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(individual_desc_1); + rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(individual_desc_2); + rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(individual_desc_3); + rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(empty_individual_desc); + rosidl_runtime_c__type_description__Field__destroy(field_1); + rosidl_runtime_c__type_description__Field__destroy(field_2); + rosidl_runtime_c__type_description__Field__destroy(field_3); + } + + rosidl_runtime_c__type_description__TypeDescription * type_description_1; + rosidl_runtime_c__type_description__TypeDescription * type_description_2; + rosidl_runtime_c__type_description__TypeDescription * type_description_3; + + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_desc_1; + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_desc_2; + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_desc_3; + rosidl_runtime_c__type_description__IndividualTypeDescription * empty_individual_desc; + + rosidl_runtime_c__type_description__Field * field_1; + rosidl_runtime_c__type_description__Field * field_2; + rosidl_runtime_c__type_description__Field * field_3; + + rcutils_ret_t ret = RCUTILS_RET_ERROR; +}; + + +TEST_F(TestUtilsFixture, test_appends_and_advanced_construction) +{ + // FIELD APPEND + EXPECT_EQ(individual_desc_1->fields.size, 0); + EXPECT_EQ(individual_desc_1->fields.capacity, 0); + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_1); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_EQ(individual_desc_1->fields.size, 1); + EXPECT_EQ(individual_desc_1->fields.capacity, 1); + + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_2); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_EQ(individual_desc_1->fields.size, 2); + EXPECT_EQ(individual_desc_1->fields.capacity, 2); + + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_3); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_EQ(individual_desc_1->fields.size, 3); + EXPECT_EQ(individual_desc_1->fields.capacity, 3); + + EXPECT_TRUE( + rosidl_runtime_c__type_description__Field__are_equal( + &individual_desc_1->fields.data[0], field_1)); + EXPECT_TRUE( + rosidl_runtime_c__type_description__Field__are_equal( + &individual_desc_1->fields.data[1], field_2)); + EXPECT_TRUE( + rosidl_runtime_c__type_description__Field__are_equal( + &individual_desc_1->fields.data[2], field_3)); + + + // REFERENCED INDIVIDUAL TYPE DESCRIPTION APPEND + // Append out of order + EXPECT_EQ(type_description_1->referenced_type_descriptions.size, 0); + EXPECT_EQ(type_description_1->referenced_type_descriptions.capacity, 0); + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_1, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + EXPECT_EQ(type_description_1->referenced_type_descriptions.size, 1); + EXPECT_EQ(type_description_1->referenced_type_descriptions.capacity, 1); + + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_3, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + EXPECT_EQ(type_description_1->referenced_type_descriptions.size, 2); + EXPECT_EQ(type_description_1->referenced_type_descriptions.capacity, 2); + + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &type_description_1->referenced_type_descriptions.data[0], individual_desc_1)); + + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &type_description_1->referenced_type_descriptions.data[1], individual_desc_3)); + + + // Append and Sort + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_2, true); + ASSERT_EQ(ret, RCUTILS_RET_OK); + EXPECT_EQ(type_description_1->referenced_type_descriptions.size, 3); + EXPECT_EQ(type_description_1->referenced_type_descriptions.capacity, 3); + + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &type_description_1->referenced_type_descriptions.data[0], individual_desc_1)); + + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &type_description_1->referenced_type_descriptions.data[1], individual_desc_2)); + + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &type_description_1->referenced_type_descriptions.data[2], individual_desc_3)); + + + // REFERENCED TYPE DESCRIPTION APPEND + // Naive recursive append + EXPECT_EQ(type_description_2->referenced_type_descriptions.size, 0); + EXPECT_EQ(type_description_2->referenced_type_descriptions.capacity, 0); + ret = rosidl_runtime_c_type_description_utils_append_referenced_type_description( + type_description_2, type_description_1, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + EXPECT_EQ(type_description_2->referenced_type_descriptions.size, 4); + EXPECT_EQ(type_description_2->referenced_type_descriptions.capacity, 4); + + + // Recursive append with coercion to valid + EXPECT_EQ( + rosidl_runtime_c_type_description_utils_append_field( + &type_description_1->type_description, field_1), + RCUTILS_RET_OK); + + EXPECT_EQ( + rosidl_runtime_c_type_description_utils_append_field( + &type_description_3->type_description, field_1), + RCUTILS_RET_OK); + + // Deliberately invalid + EXPECT_EQ(type_description_3->referenced_type_descriptions.size, 0); + EXPECT_EQ(type_description_3->referenced_type_descriptions.capacity, 0); + ASSERT_EQ( + rosidl_runtime_c_type_description_utils_append_referenced_type_description( + type_description_3, type_description_1, true), + RCUTILS_RET_WARN); + EXPECT_EQ(type_description_3->referenced_type_descriptions.size, 4); + EXPECT_EQ(type_description_3->referenced_type_descriptions.capacity, 4); + + // With the append of the empty ref description, it should work now + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, empty_individual_desc, true); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ASSERT_EQ( + rosidl_runtime_c_type_description_utils_append_referenced_type_description( + type_description_3, type_description_1, true), + RCUTILS_RET_OK); + EXPECT_EQ(type_description_3->referenced_type_descriptions.size, 1); + EXPECT_EQ(type_description_3->referenced_type_descriptions.capacity, 1); + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &type_description_3->referenced_type_descriptions.data[0], empty_individual_desc)); + + + // Get referenced type as its own full description + // Naively (naive append of ref types) + rosidl_runtime_c__type_description__TypeDescription * subset_desc = NULL; + rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( + &type_description_1->referenced_type_descriptions, // Refs: i1, i2, i3, empty + individual_desc_1, // Depends on ref: "empty" + &subset_desc, + false); // No coercion to valid + EXPECT_EQ(subset_desc->referenced_type_descriptions.size, 4); + EXPECT_EQ(subset_desc->referenced_type_descriptions.capacity, 4); + EXPECT_FALSE( + rosidl_runtime_c__type_description__TypeDescription__are_equal( + type_description_1, subset_desc)); + rosidl_runtime_c__type_description__TypeDescription__destroy(subset_desc); + + rosidl_runtime_c__type_description__TypeDescription * subset_desc_2 = NULL; + rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( + &type_description_1->referenced_type_descriptions, // Refs: i1, i2, i3, empty + individual_desc_1, // Depends on ref: "empty" + &subset_desc_2, + true); // Coercion to valid + EXPECT_EQ(subset_desc_2->referenced_type_descriptions.size, 1); + EXPECT_EQ(subset_desc_2->referenced_type_descriptions.capacity, 1); + EXPECT_FALSE( + rosidl_runtime_c__type_description__TypeDescription__are_equal( + type_description_1, subset_desc_2)); + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &subset_desc_2->referenced_type_descriptions.data[0], empty_individual_desc)); + // rosidl_runtime_c_type_description_utils_print_type_description(type_description_1); + // rosidl_runtime_c_type_description_utils_print_type_description(subset_desc_2); + rosidl_runtime_c__type_description__TypeDescription__destroy(subset_desc_2); +} + + +TEST_F(TestUtilsFixture, test_find) +{ + // Find Field + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_1); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_2); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_3); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + rosidl_runtime_c__type_description__Field * find_field = NULL; + ret = rosidl_runtime_c_type_description_utils_find_field( + &individual_desc_1->fields, "a", + &find_field); + EXPECT_EQ(ret, RCUTILS_RET_NOT_FOUND); + EXPECT_FALSE(find_field); + + ret = rosidl_runtime_c_type_description_utils_find_field( + &individual_desc_1->fields, "f3", + &find_field); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_TRUE(rosidl_runtime_c__type_description__Field__are_equal(find_field, field_3)); + + // Find Referenced Type Description + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_1, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_2, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_3, true); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + rosidl_runtime_c__type_description__IndividualTypeDescription * find_desc = NULL; + ret = rosidl_runtime_c_type_description_utils_find_referenced_type_description( + &type_description_1->referenced_type_descriptions, "a", &find_desc); + EXPECT_EQ(ret, RCUTILS_RET_NOT_FOUND); + EXPECT_FALSE(find_desc); + + ret = rosidl_runtime_c_type_description_utils_find_referenced_type_description( + &type_description_1->referenced_type_descriptions, "i3", &find_desc); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + find_desc, individual_desc_3)); +} + + +TEST_F(TestUtilsFixture, test_maps) +{ + rcutils_hash_map_t * hash_map = NULL; + rcutils_hash_map_t * ref_types_hash_map = NULL; + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + size_t map_length; + + // Field map when empty + ret = rosidl_runtime_c_type_description_utils_get_field_map( + individual_desc_1, &allocator, + &hash_map); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ASSERT_EQ(rcutils_hash_map_get_size(hash_map, &map_length), RCUTILS_RET_OK); + ASSERT_EQ(map_length, 0); + ASSERT_EQ(rcutils_hash_map_fini(hash_map), RCUTILS_RET_OK); + allocator.deallocate(hash_map, allocator.state); + hash_map = NULL; + + // Field map when populated + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_1); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_2); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_field(individual_desc_1, field_3); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ret = rosidl_runtime_c_type_description_utils_get_field_map( + individual_desc_1, &allocator, + &hash_map); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ASSERT_EQ(rcutils_hash_map_get_size(hash_map, &map_length), RCUTILS_RET_OK); + ASSERT_EQ(map_length, 3); + ASSERT_EQ(rcutils_hash_map_fini(hash_map), RCUTILS_RET_OK); + allocator.deallocate(hash_map, allocator.state); + hash_map = NULL; + + + // Ref type map when empty + ret = rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( + &type_description_1->referenced_type_descriptions, &allocator, &hash_map); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ASSERT_EQ(rcutils_hash_map_get_size(hash_map, &map_length), RCUTILS_RET_OK); + ASSERT_EQ(map_length, 0); + ASSERT_EQ(rcutils_hash_map_fini(hash_map), RCUTILS_RET_OK); + allocator.deallocate(hash_map, allocator.state); + hash_map = NULL; + + // Ref type map when populated + // Also we set up the next test block (for testing the necessary map) + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_1, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_2, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, individual_desc_3, false); + ASSERT_EQ(ret, RCUTILS_RET_OK); + ret = rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( + type_description_1, empty_individual_desc, true); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ret = rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( + &type_description_1->referenced_type_descriptions, &allocator, &ref_types_hash_map); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ASSERT_EQ(rcutils_hash_map_get_size(ref_types_hash_map, &map_length), RCUTILS_RET_OK); + ASSERT_EQ(map_length, 4); + + + // Necessary ref type map when empty + ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( + &type_description_1->type_description, + ref_types_hash_map, + &allocator, + &hash_map); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ASSERT_EQ(rcutils_hash_map_get_size(hash_map, &map_length), RCUTILS_RET_OK); + ASSERT_EQ(map_length, 0); + ASSERT_EQ(rcutils_hash_map_fini(hash_map), RCUTILS_RET_OK); + allocator.deallocate(hash_map, allocator.state); + hash_map = NULL; + + // Necessary ref type map when populated + ret = rosidl_runtime_c_type_description_utils_append_field( + &type_description_1->type_description, field_1); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + // Expect failure since empty ref type not added yet + ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( + &type_description_1->type_description, + ref_types_hash_map, + &allocator, + &hash_map); + ASSERT_EQ(ret, RCUTILS_RET_OK); + + ASSERT_EQ(rcutils_hash_map_get_size(hash_map, &map_length), RCUTILS_RET_OK); + ASSERT_EQ(map_length, 1); + ASSERT_EQ(rcutils_hash_map_fini(hash_map), RCUTILS_RET_OK); + allocator.deallocate(hash_map, allocator.state); + hash_map = NULL; + + + // Ref type map to sequence + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * seq = NULL; + ret = + rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_descriptions_map( + ref_types_hash_map, &seq, true); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_TRUE( + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__are_equal( + &type_description_1->referenced_type_descriptions, seq)); + + + rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__destroy(seq); + + ASSERT_EQ(rcutils_hash_map_fini(ref_types_hash_map), RCUTILS_RET_OK); + allocator.deallocate(ref_types_hash_map, allocator.state); + ref_types_hash_map = NULL; +} + +// TODO(methylDragon) +// TEST(Utils, test_prints) <-- deferred because it's utility +// TEST(Utils, test_validity) <-- This is technically implicitly tested with appends with coercion From 367615ff5b98a60289c2ee332e3ecfd192031eae Mon Sep 17 00:00:00 2001 From: methylDragon Date: Wed, 15 Mar 2023 22:45:07 -0700 Subject: [PATCH 02/11] Fix realloc check bug Signed-off-by: methylDragon --- .../rosidl_runtime_c/type_description_utils.h | 12 ++++---- rosidl_runtime_c/src/type_description_utils.c | 28 +++++++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h index 3358a9363..c20a4b53e 100644 --- a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h +++ b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h @@ -58,8 +58,8 @@ static const uint8_t ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DE * It will remain pointing to `NULL` if no matching `Field` is found. * * Ownership: - * - The output `Field` borrows the `fields` arg's `Field` element. It is not authorized to delete - * its value and cannot outlive the owner it borrows from. + * - The output `Field` borrows the `fields` arg's `Field` element. It is not authorized to + * deallocate it and cannot outlive the owner it borrows from. * *
* Attribute | Adherence @@ -92,7 +92,7 @@ rosidl_runtime_c_type_description_utils_find_field( * * Ownership: * - The output `IndividualTypeDescription` borrows the `referenced_types` arg's - * `IndividualTypeDescription` element. It is not authorized to delete its value and cannot + * `IndividualTypeDescription` element. It is not authorized to deallocate it and it cannot * outlive the `referenced_types` it borrows from. * *
@@ -133,7 +133,7 @@ rosidl_runtime_c_type_description_utils_find_referenced_type_description( * - The caller assumes ownership of the output `rcutils_hash_map_t` and must free it and its * elements, but NOT finalize its values. * - The output `rcutils_hash_map_t` has values that borrows the `individual_description` arg's - * `Field` objects. It is not authorized to delete its values and it cannot outlive the owner it + * `Field` objects. It is not authorized to deallocate them and it cannot outlive the owner it * borrows from. * - Finalizing the `rcutils_hash_map_t` should not result in the map's values getting finalized. * @@ -173,7 +173,7 @@ rosidl_runtime_c_type_description_utils_get_field_map( * - The caller assumes ownership of the output `rcutils_hash_map_t` and must free it and its * elements, but NOT finalize its values. * - The output `rcutils_hash_map_t` has values that borrows the `referenced_types` arg's - * `IndividualTypeDescription` elements. It is not authorized to delete its values and it cannot + * `IndividualTypeDescription` elements. It is not authorized to deallocate them and it cannot * outlive the `referenced_types` it borrows from. * - Finalizing the output `rcutils_hash_map_t` should not result in its values getting finalized. * @@ -224,7 +224,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( * - The caller assumes ownership of the output `rcutils_hash_map_t` and must free it and its * elements, but NOT finalize its values. * - The output `rcutils_hash_map_t` has values that borrows the `referenced_types_map` arg's - * `IndividualTypeDescription` values. It is not authorized to delete its values and it cannot + * `IndividualTypeDescription` values. It is not authorized to deallocate them and it cannot * outlive owner it borrows from. * - Finalizing the output `rcutils_hash_map_t` should not result in its values getting finalized. * diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index 5af5d5658..152b918d0 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -586,7 +586,7 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate(referenced_types->data, allocation_size, allocator.state); - if (next_ptr == NULL) { + if (next_ptr == NULL && allocation_size != 0) { RCUTILS_LOG_ERROR( "Could not shrink the necessary referenced type descriptions sequence during rearrangement! " "Beware! The referenced type descriptions was likely already partially modified in place!"); @@ -897,6 +897,10 @@ rosidl_runtime_c_type_description_utils_create_field( } *field = rosidl_runtime_c__type_description__Field__create(); + if (*field == NULL) { + RCUTILS_LOG_ERROR("Could not create field"); + return RCUTILS_RET_BAD_ALLOC; + } // Field if (!rosidl_runtime_c__String__assignn(&(*field)->name, name, name_length)) { @@ -944,6 +948,10 @@ rosidl_runtime_c_type_description_utils_create_individual_type_description( } *individual_description = rosidl_runtime_c__type_description__IndividualTypeDescription__create(); + if (*individual_description == NULL) { + RCUTILS_LOG_ERROR("Could not create individual description"); + return RCUTILS_RET_BAD_ALLOC; + } if (!rosidl_runtime_c__String__assignn( &(*individual_description)->type_name, type_name, type_name_length)) @@ -969,6 +977,10 @@ rosidl_runtime_c_type_description_utils_create_type_description( } *type_description = rosidl_runtime_c__type_description__TypeDescription__create(); + if (*type_description == NULL) { + RCUTILS_LOG_ERROR("Could not create type description"); + return RCUTILS_RET_BAD_ALLOC; + } if (!rosidl_runtime_c__String__assignn( &(*type_description)->type_description.type_name, type_name, type_name_length)) @@ -1000,7 +1012,7 @@ rosidl_runtime_c_type_description_utils_append_field( size_t last_index = individual_type_description->fields.size; rosidl_runtime_c__type_description__Field * next_ptr = allocator.reallocate( individual_type_description->fields.data, allocation_size, allocator.state); - if (next_ptr == NULL) { + if (next_ptr == NULL && allocation_size != 0) { RCUTILS_LOG_ERROR("Could not realloc individual type description fields sequence"); return RCUTILS_RET_BAD_ALLOC; } @@ -1029,7 +1041,7 @@ rosidl_runtime_c_type_description_utils_append_field( individual_type_description->fields.data, individual_type_description->fields.size * sizeof(rosidl_runtime_c__type_description__Field), allocator.state); - if (next_ptr == NULL) { + if (next_ptr == NULL && individual_type_description->fields.size != 0) { RCUTILS_LOG_ERROR( "Could not shorten individual type description fields sequence. " "Excess memory will be UNINITIALIZED!"); @@ -1062,7 +1074,7 @@ rosidl_runtime_c_type_description_utils_append_referenced_individual_type_descri rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate( type_description->referenced_type_descriptions.data, allocation_size, allocator.state); - if (next_ptr == NULL) { + if (next_ptr == NULL && allocation_size != 0) { RCUTILS_LOG_ERROR("Could not realloc type description referenced type descriptions sequence"); return RCUTILS_RET_BAD_ALLOC; } @@ -1106,7 +1118,7 @@ rosidl_runtime_c_type_description_utils_append_referenced_individual_type_descri sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription) ), allocator.state); - if (next_ptr == NULL) { + if (next_ptr == NULL && type_description->referenced_type_descriptions.size != 0) { RCUTILS_LOG_ERROR( "Could not shorten type description referenced type descriptions sequence. " "Excess memory will be UNINITIALIZED!"); @@ -1139,7 +1151,7 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( ); rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate( type_description->referenced_type_descriptions.data, allocation_size, allocator.state); - if (next_ptr == NULL) { + if (next_ptr == NULL && allocation_size != 0) { RCUTILS_LOG_ERROR("Could not realloc type description referenced type descriptions sequence"); return RCUTILS_RET_BAD_ALLOC; } @@ -1207,7 +1219,7 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( sizeof(rosidl_runtime_c__type_description__IndividualTypeDescription) ), allocator.state); - if (next_ptr == NULL) { + if (next_ptr == NULL && type_description->referenced_type_descriptions.size != 0) { RCUTILS_LOG_ERROR( "Could not shorten type description referenced type descriptions sequence. " "Excess memory will be UNINITIALIZED!"); @@ -1259,7 +1271,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ if (coerce_to_valid) { rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( - *output_description); + *output_description); if (ret != RCUTILS_RET_OK) { RCUTILS_LOG_ERROR("Could not coerce output type description to valid"); rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); From 956a7ec5d5665139f8cd30a240dcc319ccc98f1d Mon Sep 17 00:00:00 2001 From: methylDragon Date: Thu, 16 Mar 2023 18:16:37 -0700 Subject: [PATCH 03/11] Fix const and bool bug Signed-off-by: methylDragon --- .../rosidl_runtime_c/type_description_utils.h | 15 +++++++++------ rosidl_runtime_c/src/type_description_utils.c | 13 +++++++------ .../test/test_type_description_utils.cpp | 1 + 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h index c20a4b53e..0de341eb7 100644 --- a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h +++ b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h @@ -341,7 +341,7 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl ROSIDL_GENERATOR_C_PUBLIC bool rosidl_runtime_c_type_description_utils_field_is_valid( - rosidl_runtime_c__type_description__Field * field); + const rosidl_runtime_c__type_description__Field * field); /// Check if individual type description is valid /** @@ -354,7 +354,7 @@ rosidl_runtime_c_type_description_utils_field_is_valid( ROSIDL_GENERATOR_C_PUBLIC bool rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( - rosidl_runtime_c__type_description__IndividualTypeDescription * description); + const rosidl_runtime_c__type_description__IndividualTypeDescription * description); /// Check if type description is valid /** @@ -370,7 +370,7 @@ rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( ROSIDL_GENERATOR_C_PUBLIC bool rosidl_runtime_c_type_description_utils_type_description_is_valid( - rosidl_runtime_c__type_description__TypeDescription * description); + const rosidl_runtime_c__type_description__TypeDescription * description); /// This is on a best effort basis, it won't work if the fields of the main or necessary referenced /// types are invalid. It prunes then sorts. @@ -597,12 +597,15 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( rosidl_runtime_c__type_description__TypeDescription * type_description_to_append, bool coerce_to_valid); -// Copy main type description, then validate if coerce_to_valid is true +// Create a type description from a referenced description, then validate if coerce_to_valid is true +// This is done by copy!! This allocates memory and the caller is responsible for deallocating the +// output ROSIDL_GENERATOR_C_PUBLIC rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( - rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_descriptions, - rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * + referenced_descriptions, + const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, rosidl_runtime_c__type_description__TypeDescription ** output_description, bool coerce_to_valid); diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index 152b918d0..eb07e384d 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -622,7 +622,7 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl bool rosidl_runtime_c_type_description_utils_field_is_valid( - rosidl_runtime_c__type_description__Field * field) + const rosidl_runtime_c__type_description__Field * field) { if (field == NULL) { RCUTILS_LOG_WARN("Field is invalid: Pointer is null"); @@ -651,7 +651,7 @@ rosidl_runtime_c_type_description_utils_field_is_valid( bool rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( - rosidl_runtime_c__type_description__IndividualTypeDescription * description) + const rosidl_runtime_c__type_description__IndividualTypeDescription * description) { if (description == NULL) { RCUTILS_LOG_WARN("Individual type description is invalid: Pointer is null"); @@ -710,7 +710,7 @@ rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( bool rosidl_runtime_c_type_description_utils_type_description_is_valid( - rosidl_runtime_c__type_description__TypeDescription * description) + const rosidl_runtime_c__type_description__TypeDescription * description) { if (description == NULL) { RCUTILS_LOG_WARN("Type description is invalid: Pointer is null"); @@ -799,7 +799,7 @@ rosidl_runtime_c_type_description_utils_type_description_is_valid( RCUTILS_LOG_ERROR("Could allocate sequence for copy of referenced type descriptions"); goto end_necessary; } - if (rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__copy( + if (!rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__copy( &description->referenced_type_descriptions, sorted_sequence)) { RCUTILS_LOG_ERROR("Could not copy referenced type descriptions for validation"); @@ -1232,8 +1232,9 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( - rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_descriptions, - rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * + referenced_descriptions, + const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, rosidl_runtime_c__type_description__TypeDescription ** output_description, bool coerce_to_valid) { diff --git a/rosidl_runtime_c/test/test_type_description_utils.cpp b/rosidl_runtime_c/test/test_type_description_utils.cpp index 50e908f95..94255c876 100644 --- a/rosidl_runtime_c/test/test_type_description_utils.cpp +++ b/rosidl_runtime_c/test/test_type_description_utils.cpp @@ -365,6 +365,7 @@ TEST_F(TestUtilsFixture, test_appends_and_advanced_construction) individual_desc_1, // Depends on ref: "empty" &subset_desc_2, true); // Coercion to valid + EXPECT_TRUE(rosidl_runtime_c_type_description_utils_type_description_is_valid(subset_desc_2)); EXPECT_EQ(subset_desc_2->referenced_type_descriptions.size, 1); EXPECT_EQ(subset_desc_2->referenced_type_descriptions.capacity, 1); EXPECT_FALSE( From 569a75b0c3ecc1bea8863acbc33d1b645a34940b Mon Sep 17 00:00:00 2001 From: methylDragon Date: Wed, 22 Mar 2023 15:11:52 -0700 Subject: [PATCH 04/11] Implement type name string replacement Signed-off-by: methylDragon --- .../rosidl_runtime_c/type_description_utils.h | 44 ++++++- rosidl_runtime_c/src/type_description_utils.c | 108 +++++++++++++++++- .../test/test_type_description_utils.cpp | 19 ++- 3 files changed, 162 insertions(+), 9 deletions(-) diff --git a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h index 0de341eb7..c5a3c99c3 100644 --- a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h +++ b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h @@ -604,11 +604,53 @@ ROSIDL_GENERATOR_C_PUBLIC rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * - referenced_descriptions, + referenced_descriptions, const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, rosidl_runtime_c__type_description__TypeDescription ** output_description, bool coerce_to_valid); +// Create a type description from a referenced description, then validate if coerce_to_valid is true +// This is done by copy!! This allocates memory and the caller is responsible for deallocating the +// output +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( + const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * + referenced_descriptions, + const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, + rosidl_runtime_c__type_description__TypeDescription ** output_description, + bool coerce_to_valid); + +/// In-place replace substrings in an individual description's type name and nested names in fields +/** + * This means the `IndividualTypeDescription`'s' type_name will get replaced and all references + * to any nested_type_names in the description's fields. + * + * NOTE(methylDragon): This method is pretty inefficient because it doesn't do checking and will + * re-copy all names. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, + const char * from, + const char * to); + + +/// In-place replace substrings across all type names (and references to those names) +/** + * This means all `IndividualTypeDescription` type_names will get replaced, in the main description + * and referenced type descriptions, and also all references to those names (nested_type_name.) + * + * NOTE(methylDragon): This method is pretty inefficient because it doesn't do checking and will + * re-copy all names. + */ +ROSIDL_GENERATOR_C_PUBLIC +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_place( + rosidl_runtime_c__type_description__TypeDescription * type_description, + const char * from, + const char * to); // ================================================================================================= // DESCRIPTION PRINTING diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index eb07e384d..205f4025c 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -1233,7 +1234,7 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * - referenced_descriptions, + referenced_descriptions, const rosidl_runtime_c__type_description__IndividualTypeDescription * referenced_description, rosidl_runtime_c__type_description__TypeDescription ** output_description, bool coerce_to_valid) @@ -1272,7 +1273,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ if (coerce_to_valid) { rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( - *output_description); + *output_description); if (ret != RCUTILS_RET_OK) { RCUTILS_LOG_ERROR("Could not coerce output type description to valid"); rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); @@ -1284,6 +1285,109 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ } +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, + const char * from, + const char * to) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(individual_type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(from, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(to, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + bool ret = false; + char * repl = NULL; + + // Replace type name + repl = rcutils_repl_str( + individual_type_description->type_name.data, from, to, &allocator); + if (repl == NULL) { + RCUTILS_LOG_ERROR("Could not replace individual type description type name"); + return RCUTILS_RET_ERROR; + } + + ret = rosidl_runtime_c__String__assign(&(individual_type_description->type_name), repl); + allocator.deallocate(repl, allocator.state); + if (!ret) { + RCUTILS_LOG_ERROR("Could not assign individual type description type name"); + return RCUTILS_RET_ERROR; + } + + // Replace field nested type names + rosidl_runtime_c__type_description__Field * field = NULL; + if (individual_type_description->fields.data) { + for (size_t i = 0; i < individual_type_description->fields.size; i++) { + field = &individual_type_description->fields.data[i]; + if (!field->type.nested_type_name.size) { + continue; + } + + repl = rcutils_repl_str(field->type.nested_type_name.data, from, to, &allocator); + if (repl == NULL) { + RCUTILS_LOG_ERROR( + "Could not replace individual type description field nested type name. Beware! " + "Partial in-place replacements might have already happened!"); + return RCUTILS_RET_ERROR; + } + + ret = rosidl_runtime_c__String__assign(&(field->type.nested_type_name), repl); + allocator.deallocate(repl, allocator.state); + if (!ret) { + RCUTILS_LOG_ERROR( + "Could not assign individual type description field nested type name. Beware! " + "Partial in-place replacements might have already happened!"); + return RCUTILS_RET_ERROR; + } + } + } + return RCUTILS_RET_OK; +} + + +rcutils_ret_t +rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_place( + rosidl_runtime_c__type_description__TypeDescription * type_description, + const char * from, + const char * to) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_description, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(from, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(to, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_ret_t end_ret = RCUTILS_RET_ERROR; + + end_ret = + rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( + &type_description->type_description, from, to); + if (end_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR("Could not replace main type description type name"); + return end_ret; + } + + if (type_description->referenced_type_descriptions.data) { + for (size_t i = 0; i < type_description->referenced_type_descriptions.size; i++) { + rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description = + &type_description->referenced_type_descriptions.data[i]; + + /* *INDENT-OFF* */ // Uncrustify will dislodge the NOLINT + end_ret = + rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( // NOLINT + individual_type_description, from, to); + /* *INDENT-ON* */ + + if (end_ret != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR( + "Could not replace type names in referenced type. Beware! " + "Partial in-place replacements might have already happened!"); + return end_ret; + } + } + } + return RCUTILS_RET_OK; +} + + // ================================================================================================= // DESCRIPTION PRINTING // ================================================================================================= diff --git a/rosidl_runtime_c/test/test_type_description_utils.cpp b/rosidl_runtime_c/test/test_type_description_utils.cpp index 94255c876..ea23f66fb 100644 --- a/rosidl_runtime_c/test/test_type_description_utils.cpp +++ b/rosidl_runtime_c/test/test_type_description_utils.cpp @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include + #include "rosidl_runtime_c/type_description/field__functions.h" #include "rosidl_runtime_c/type_description/field__struct.h" #include "rosidl_runtime_c/type_description/individual_type_description__functions.h" @@ -19,9 +22,6 @@ #include "rosidl_runtime_c/type_description/type_description__functions.h" #include "rosidl_runtime_c/type_description/type_description__struct.h" #include "rosidl_runtime_c/type_description_utils.h" - -#include -#include #include #include @@ -127,7 +127,7 @@ TEST(TestUtils, test_basic_construction) } -class TestUtilsFixture: public ::testing::Test +class TestUtilsFixture : public ::testing::Test { public: void SetUp() @@ -374,8 +374,15 @@ TEST_F(TestUtilsFixture, test_appends_and_advanced_construction) EXPECT_TRUE( rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( &subset_desc_2->referenced_type_descriptions.data[0], empty_individual_desc)); - // rosidl_runtime_c_type_description_utils_print_type_description(type_description_1); - // rosidl_runtime_c_type_description_utils_print_type_description(subset_desc_2); + + // Test type name string replacements + ret = rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_place( + subset_desc_2, "mpty", "ligibility" // Expect eligibility + ); + EXPECT_EQ(ret, RCUTILS_RET_OK); + EXPECT_FALSE( + rosidl_runtime_c__type_description__IndividualTypeDescription__are_equal( + &subset_desc_2->referenced_type_descriptions.data[0], empty_individual_desc)); rosidl_runtime_c__type_description__TypeDescription__destroy(subset_desc_2); } From 88af94f0c775d1f919bd5a0cee54416f0bb48ea7 Mon Sep 17 00:00:00 2001 From: methylDragon Date: Sat, 1 Apr 2023 05:21:20 -0700 Subject: [PATCH 05/11] Lint Signed-off-by: methylDragon --- .../test/test_type_description_utils.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/rosidl_runtime_c/test/test_type_description_utils.cpp b/rosidl_runtime_c/test/test_type_description_utils.cpp index ea23f66fb..184df7388 100644 --- a/rosidl_runtime_c/test/test_type_description_utils.cpp +++ b/rosidl_runtime_c/test/test_type_description_utils.cpp @@ -12,8 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include + #include #include +#include + +#include #include "rosidl_runtime_c/type_description/field__functions.h" #include "rosidl_runtime_c/type_description/field__struct.h" @@ -22,12 +28,6 @@ #include "rosidl_runtime_c/type_description/type_description__functions.h" #include "rosidl_runtime_c/type_description/type_description__struct.h" #include "rosidl_runtime_c/type_description_utils.h" -#include - -#include -#include - -#include TEST(TestUtils, test_basic_construction) @@ -545,9 +545,10 @@ TEST_F(TestUtilsFixture, test_maps) // Ref type map to sequence rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * seq = NULL; - ret = - rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_descriptions_map( + /* *INDENT-OFF* */ + ret = rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_descriptions_map( // NOLINT ref_types_hash_map, &seq, true); + /* *INDENT-ON* */ EXPECT_EQ(ret, RCUTILS_RET_OK); EXPECT_TRUE( rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__are_equal( From 7f2a61a61f940b4a6159030f5daf7e0868e1bd12 Mon Sep 17 00:00:00 2001 From: methylDragon Date: Mon, 3 Apr 2023 02:15:48 -0700 Subject: [PATCH 06/11] Fix windows build Signed-off-by: methylDragon --- rosidl_runtime_c/src/type_description_utils.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index 205f4025c..41183a89c 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -41,17 +41,17 @@ #include "rosidl_runtime_c/type_description/type_description__struct.h" -// Modified from https://stackoverflow.com/questions/4398711/round-to-the-nearest-power-of-two -int next_power_of_two(int v) +// Modified from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +size_t next_power_of_two(size_t v) { v--; + size_t shf = 1; v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; + for (size_t i = 0; i < sizeof(size_t); i++) { + shf = shf * 2; + v |= v >> shf; + } v++; // next power of 2 - return v > 1 ? v : 1; } From bed724e44495011c928d5760484fc0faca5be76e Mon Sep 17 00:00:00 2001 From: methylDragon Date: Mon, 3 Apr 2023 16:59:13 -0700 Subject: [PATCH 07/11] Fix print Signed-off-by: methylDragon --- rosidl_runtime_c/src/type_description_utils.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index 41183a89c..429cb11ad 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -22,6 +22,7 @@ #include "rosidl_runtime_c/type_description_utils.h" +#include #include #include #include @@ -1399,8 +1400,8 @@ rosidl_runtime_c_type_description_utils_print_field_type( printf( " [FIELD TYPE]\n" " type_id: %d\n" - " capacity: %ld\n" - " string_capacity: %ld\n", + " capacity: %" PRIu64 "\n" + " string_capacity: %" PRIu64 "\n", field_type->type_id, field_type->capacity, field_type->string_capacity); if (field_type->nested_type_name.data == NULL) { @@ -1438,7 +1439,7 @@ rosidl_runtime_c_type_description_utils_print_individual_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description) { printf( - "\n[INDIVIDUAL TYPE DESCRIPTION] (Fields: %ld)\n", individual_type_description->fields.size); + "\n[INDIVIDUAL TYPE DESCRIPTION] (Fields: %zd)\n", individual_type_description->fields.size); if (individual_type_description->type_name.data == NULL) { printf(" type_name: %s\n", individual_type_description->type_name.data); @@ -1459,7 +1460,7 @@ void rosidl_runtime_c_type_description_utils_print_type_description( printf("\n\n---\n\n"); printf( - "= [PRINTING TYPE DESCRIPTION] = (Referenced descriptions: %ld)\n", + "= [PRINTING TYPE DESCRIPTION] = (Referenced descriptions: %zd)\n", type_description->referenced_type_descriptions.size); printf("\n== [MAIN DESCRIPTION] ==\n"); From a52c15a7282c2f9e1ea96886399a9aa5dbf34511 Mon Sep 17 00:00:00 2001 From: methylDragon Date: Mon, 3 Apr 2023 21:44:45 -0700 Subject: [PATCH 08/11] Fix print (for nulls) Signed-off-by: methylDragon --- rosidl_runtime_c/src/type_description_utils.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index 429cb11ad..e4643bf0a 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -1405,7 +1405,7 @@ rosidl_runtime_c_type_description_utils_print_field_type( field_type->type_id, field_type->capacity, field_type->string_capacity); if (field_type->nested_type_name.data == NULL) { - printf(" nested_type_name: %s\n", field_type->nested_type_name.data); + printf(" nested_type_name: (null)\n"); } else { printf(" nested_type_name: \"%s\"\n", field_type->nested_type_name.data); } @@ -1419,13 +1419,13 @@ rosidl_runtime_c_type_description_utils_print_field( printf("[FIELD]\n"); if (field->name.data == NULL) { - printf(" name: %s\n", field->name.data); + printf(" name: (null)\n"); } else { printf(" name: \"%s\"\n", field->name.data); } if (field->default_value.data == NULL) { - printf(" default_value: %s\n", field->default_value.data); + printf(" default_value: (null)\n"); } else { printf(" default_value: \"%s\"\n", field->default_value.data); } @@ -1442,7 +1442,7 @@ rosidl_runtime_c_type_description_utils_print_individual_type_description( "\n[INDIVIDUAL TYPE DESCRIPTION] (Fields: %zd)\n", individual_type_description->fields.size); if (individual_type_description->type_name.data == NULL) { - printf(" type_name: %s\n", individual_type_description->type_name.data); + printf(" type_name: (null)\n"); } else { printf(" type_name: \"%s\"\n", individual_type_description->type_name.data); } @@ -1485,7 +1485,7 @@ rosidl_runtime_c_type_description_utils_print_field_map(const rcutils_hash_map_t rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); while (RCUTILS_RET_OK == status) { if (key == NULL) { - printf("\n== KEY: %s ==\n", key); + printf("\n== KEY: (null) ==\n"); } else { printf("\n== KEY: \"%s\" ==\n", key); } @@ -1504,7 +1504,7 @@ rosidl_runtime_c_type_description_utils_print_referenced_type_description_map( rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); while (RCUTILS_RET_OK == status) { if (key == NULL) { - printf("\n== KEY: %s ==\n", key); + printf("\n== KEY: (null) ==\n"); } else { printf("\n== KEY: \"%s\" ==\n", key); } From b759f9429627098d908d7b596847f1073f260549 Mon Sep 17 00:00:00 2001 From: methylDragon Date: Thu, 6 Apr 2023 16:47:47 -0700 Subject: [PATCH 09/11] Refactor type description utils to use return types Signed-off-by: methylDragon --- .../rosidl_runtime_c/type_description_utils.h | 77 ++- rosidl_runtime_c/src/type_description_utils.c | 611 ++++++++++-------- .../test/test_type_description_utils.cpp | 8 +- 3 files changed, 378 insertions(+), 318 deletions(-) diff --git a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h index c5a3c99c3..ae92e080e 100644 --- a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h +++ b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -/** rosidl_runtime_c_type_description_utils: Utilities for manipulating type_description_interfaces - * C message structs +/** + * \file Utilities for manipulating type_description_interfaces C message structs. + * + * Every instance of a non-message struct (e.g. hash map) borrows, whereas the message structs copy. + * Hence, lifetime should be managed by the message structs. */ -// NOTE(methylDragon): I made it so that every instance of a non-message struct (e.g. hash map) -// borrows, whereas the message structs copy. -// So lifetime should be managed by the message structs. - #ifndef ROSIDL_RUNTIME_C__TYPE_DESCRIPTION_UTILS_H_ #define ROSIDL_RUNTIME_C__TYPE_DESCRIPTION_UTILS_H_ @@ -39,14 +38,12 @@ #include "rosidl_runtime_c/type_description/type_description__functions.h" #include "rosidl_runtime_c/type_description/type_description__struct.h" - #ifdef __cplusplus extern "C" { #endif -static const uint8_t ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER = 48; - +static const uint8_t ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_MASK = 48; // ================================================================================================= // GET BY NAME @@ -54,8 +51,8 @@ static const uint8_t ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DE /// Get the first Field, matching by name. /** - * NOTE: The `field` output arg must be passed in pointing to `NULL`. - * It will remain pointing to `NULL` if no matching `Field` is found. + * The `field` output arg must be passed in pointing to `NULL`. + * It will remain pointing to `NULL` if no matching `Field` is found. * * Ownership: * - The output `Field` borrows the `fields` arg's `Field` element. It is not authorized to @@ -72,7 +69,7 @@ static const uint8_t ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DE * \param[in] fields array of fields to search through * \param[in] name field name of the field to look for * \param[out] field the first field with field name that matches the name arg. - * Must point to `NULL`, outputs pointing to `NULL` if not found. + * Must point to `NULL`, outputs pointing to `NULL` if not found. * \return #RCUTILS_RET_OK if successful, or * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or * \return #RCUTILS_RET_NOT_FOUND if no `Field` with a matching name is found, or @@ -87,13 +84,14 @@ rosidl_runtime_c_type_description_utils_find_field( /// Get the first referenced IndividualTypeDescription, matching by type name. /** - * NOTE: The `referenced_type` output arg must be passed in pointing `NULL`. - * It will remain pointing to `NULL` if no matching `IndividualTypeDescription` is found. + * The `referenced_type` output arg must be passed in pointing `NULL`. + * It will remain pointing to `NULL` if no matching `IndividualTypeDescription` is found. * * Ownership: * - The output `IndividualTypeDescription` borrows the `referenced_types` arg's - * `IndividualTypeDescription` element. It is not authorized to deallocate it and it cannot - * outlive the `referenced_types` it borrows from. + * `IndividualTypeDescription` element. + * It is not authorized to deallocate it and it cannot outlive the `referenced_types` it borrows + * from. * *
* Attribute | Adherence @@ -106,8 +104,7 @@ rosidl_runtime_c_type_description_utils_find_field( * \param[in] referenced_types array of referenced individual type descriptions to search through * \param[in] type_name name of the referenced referenced individual type description to look for * \param[out] referenced_type the first individual type description with type name that matches the - * type_name arg. Must point to `NULL`, outputs pointing to `NULL` if - * not found. + * type_name arg. Must point to `NULL`, outputs pointing to `NULL` if not found. * \return #RCUTILS_RET_OK if successful, or * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or * \return #RCUTILS_RET_NOT_FOUND if no `IndividualTypeDescription` with matching name is found, or @@ -148,8 +145,7 @@ rosidl_runtime_c_type_description_utils_find_referenced_type_description( * \param[in] individual_description the individual type description to get the fields from * \param[in] allocator the allocator to use through out the lifetime of the hash_map * \param[out] hash_map `rcutils_hash_map_t` to be initialized, containing `Field` objects keyed by - * their field names. Must point to `NULL`, outputs pointing to `NULL` - * if error. + * their field names. Must point to `NULL`, outputs pointing to `NULL` if error. * \return #RCUTILS_RET_OK if successful, or * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or @@ -165,9 +161,9 @@ rosidl_runtime_c_type_description_utils_get_field_map( /// Construct a hash map of an `IndividualTypeDescription__Sequence`'s `IndividualTypeDescription` /// objects, keyed by type name. /** - * NOTE: The `hash_map` output arg must be passed in pointing to `NULL`. - * Furthermore, if the input `referenced_types` sequence has types with identical names but - * differing structures, this function will return `RCUTILS_RET_INVALID_ARGUMENT` and fail. + * The `hash_map` output arg must be passed in pointing to `NULL`. + * Furthermore, if the input `referenced_types` sequence has types with identical names but + * differing structures, this function will return `RCUTILS_RET_INVALID_ARGUMENT` and fail. * * Ownership: * - The caller assumes ownership of the output `rcutils_hash_map_t` and must free it and its @@ -189,8 +185,8 @@ rosidl_runtime_c_type_description_utils_get_field_map( * types from * \param[in] allocator the allocator to use through out the lifetime of the hash_map * \param[out] hash_map `rcutils_hash_map_t` to be initialized, containing - * `IndividualTypeDescription` objects keyed by their type names. Must point to - * `NULL`, outputs pointing to `NULL` if error. + * `IndividualTypeDescription` objects keyed by their type names. + * Must point to `NULL`, outputs pointing to `NULL` if error. * \return #RCUTILS_RET_OK if successful, or * \return #RCUTILS_RET_BAD_ALLOC if memory allocation fails, or * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or @@ -210,8 +206,8 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( /// Return a map of only the referenced type descriptions that are recursively necessary. /** - * NOTE: The `seen_map` output arg must be passed in pointing to `NULL`. It's a parameter so it can - * be passed into subsequent recursive calls to traverse nested types. + * The `seen_map` output arg must be passed in pointing to `NULL`. + * It's a parameter so it can be passed into subsequent recursive calls to traverse nested types. * * A referenced type description is recursively necessary if it is either: * - Needed by a field of the main IndividualTypeDescription @@ -250,9 +246,8 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( * * \param[in] main_type_description the main individual type description to check the fields of * \param[in] referenced_types_map a map of referenced `IndividualTypeDescription` objects from the - * main individual type description's parent `TypeDescription` - * object, keyed by their type names - * \param[in] allocator the allocator to use through out the lifetime of the hash_map + * main individual type description's parent `TypeDescription` object, keyed by their type names. + * \param[in] allocator the allocator to use through out the lifetime of the hash_map. * \param[in,out] seen_map `rcutils_hash_map_t` of seen necessary `IndividualTypeDescription` * objects keyed by their type names. Used in recursive calls. Must point to * `NULL` for user's top level call, outputs pointing to `NULL` if error. @@ -272,7 +267,7 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio /// Deep copy a map of individual type descriptions into a new IndividualTypeDescription__Sequence. /** - * NOTE: The `sequence` output arg must be passed in pointing to `NULL`. + * The `sequence` output arg must be passed in pointing to `NULL`. * * This method also validates that each IndividualTypeDescription in the map has a type name that * matches the key it was indexed by. @@ -293,8 +288,8 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio * * \param[in] hash_map the referenced type descriptions map to convert (via deep copy) to a sequence * \param[out] sequence the `IndividualTypeDescription__Sequence` containing copies of the - * `IndividualTypeDescription` objects stored in the values of the input - * `hash_map` arg. Must point to `NULL`, outputs pointing to `NULL` if error. + * `IndividualTypeDescription` objects stored in the values of the input `hash_map` arg. + * Must point to `NULL`, outputs pointing to `NULL` if error. * \param[in] sort sorts the referenced type descriptions if true, best effort * \return #RCUTILS_RET_OK if successful, or * \return #RCUTILS_RET_INVALID_ARGUMENT for invalid arguments, or @@ -320,9 +315,12 @@ rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_pla rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * sequence); /// Remove unnecessary referenced type descriptions from a sequence of referenced types. -/// IndividualTypeDescription elements are COPY ASSIGNED in-place, and the original sequence is -/// shrunken afterwards. -/// NOTE: DOES NOT SORT AFTER PRUNING! Call sort separately. +/** + * IndividualTypeDescription elements are COPY ASSIGNED in-place, and the original sequence is + * shrunken afterwards. + * + * DOES NOT SORT AFTER PRUNING! Call sort separately. + */ ROSIDL_GENERATOR_C_PUBLIC rcutils_ret_t rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_place( @@ -339,7 +337,7 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl * - Has a non-empty nested type name if nested */ ROSIDL_GENERATOR_C_PUBLIC -bool +rcutils_ret_t rosidl_runtime_c_type_description_utils_field_is_valid( const rosidl_runtime_c__type_description__Field * field); @@ -352,7 +350,7 @@ rosidl_runtime_c_type_description_utils_field_is_valid( * - It does not have duplicate fields */ ROSIDL_GENERATOR_C_PUBLIC -bool +rcutils_ret_t rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( const rosidl_runtime_c__type_description__IndividualTypeDescription * description); @@ -368,7 +366,7 @@ rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( * - Sorted */ ROSIDL_GENERATOR_C_PUBLIC -bool +rcutils_ret_t rosidl_runtime_c_type_description_utils_type_description_is_valid( const rosidl_runtime_c__type_description__TypeDescription * description); @@ -652,6 +650,7 @@ rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_ const char * from, const char * to); + // ================================================================================================= // DESCRIPTION PRINTING // ================================================================================================= diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index e4643bf0a..0da7b0d44 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -12,14 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// NOTE(methylDragon): Maybe this belongs inside the type_description_interfaces -// package instead? -// But as a C header (.h)??? - -// NOTE(methylDragon): I made it so that every instance of a non-message struct (e.g. hash map) -// borrows, whereas the message structs copy. -// So lifetime should be managed by the message structs. - #include "rosidl_runtime_c/type_description_utils.h" #include @@ -41,25 +33,22 @@ #include "rosidl_runtime_c/type_description/type_description__functions.h" #include "rosidl_runtime_c/type_description/type_description__struct.h" - // Modified from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 size_t next_power_of_two(size_t v) { + size_t shf = 0; v--; - size_t shf = 1; - v |= v >> 1; - for (size_t i = 0; i < sizeof(size_t); i++) { - shf = shf * 2; + for (size_t i = 0; shf < sizeof(size_t) * 4; ++i) { + shf = (1 << i); v |= v >> shf; } - v++; // next power of 2 + v++; return v > 1 ? v : 1; } // ================================================================================================= // GET BY NAME // ================================================================================================= - rcutils_ret_t rosidl_runtime_c_type_description_utils_find_field( const rosidl_runtime_c__type_description__Field__Sequence * fields, @@ -70,22 +59,21 @@ rosidl_runtime_c_type_description_utils_find_field( RCUTILS_CHECK_ARGUMENT_FOR_NULL(name, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(field, RCUTILS_RET_INVALID_ARGUMENT); if (*field != NULL) { - RCUTILS_LOG_ERROR("`field` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'field' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } - for (size_t i = 0; i < fields->size; i++) { + for (size_t i = 0; i < fields->size; ++i) { if (strcmp(fields->data[i].name.data, name) == 0) { *field = &fields->data[i]; return RCUTILS_RET_OK; } } - RCUTILS_LOG_WARN("Could not find field: %s", name); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("Could not find field: %s", name); return RCUTILS_RET_NOT_FOUND; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_find_referenced_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types, @@ -96,18 +84,19 @@ rosidl_runtime_c_type_description_utils_find_referenced_type_description( RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_name, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_type, RCUTILS_RET_INVALID_ARGUMENT); if (*referenced_type != NULL) { - RCUTILS_LOG_ERROR("`referenced_type` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'referenced_type' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } - for (size_t i = 0; i < referenced_types->size; i++) { + for (size_t i = 0; i < referenced_types->size; ++i) { if (strcmp(referenced_types->data[i].type_name.data, type_name) == 0) { *referenced_type = &referenced_types->data[i]; return RCUTILS_RET_OK; } } - RCUTILS_LOG_WARN("Could not find referenced type description: %s", type_name); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not find referenced type description: %s", type_name); return RCUTILS_RET_NOT_FOUND; } @@ -115,7 +104,6 @@ rosidl_runtime_c_type_description_utils_find_referenced_type_description( // ================================================================================================= // HASH MAPS // ================================================================================================= - rcutils_ret_t rosidl_runtime_c_type_description_utils_get_field_map( const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_description, @@ -126,19 +114,18 @@ rosidl_runtime_c_type_description_utils_get_field_map( RCUTILS_CHECK_ARGUMENT_FOR_NULL(allocator, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(hash_map, RCUTILS_RET_INVALID_ARGUMENT); if (*hash_map != NULL) { - RCUTILS_LOG_ERROR("`hash_map` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'hash_map' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } rcutils_hash_map_t * out = allocator->allocate(sizeof(rcutils_hash_map_t), allocator->state); if (out == NULL) { - RCUTILS_LOG_ERROR("Could not allocate output hash map"); + RCUTILS_SET_ERROR_MSG("Could not allocate output hash map"); return RCUTILS_RET_BAD_ALLOC; } *out = rcutils_get_zero_initialized_hash_map(); rcutils_ret_t ret = RCUTILS_RET_ERROR; - rcutils_ret_t fail_ret = RCUTILS_RET_ERROR; ret = rcutils_hash_map_init( out, next_power_of_two(individual_description->fields.size), @@ -147,19 +134,23 @@ rosidl_runtime_c_type_description_utils_get_field_map( allocator); if (ret != RCUTILS_RET_OK) { allocator->deallocate(out, allocator->state); - RCUTILS_LOG_ERROR("Could not init hash map"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("Could not init hash map:\n%s", error_string.str); return ret; } - for (size_t i = 0; i < individual_description->fields.size; i++) { + for (size_t i = 0; i < individual_description->fields.size; ++i) { rosidl_runtime_c__type_description__Field * tmp = &individual_description->fields.data[i]; // Passing tmp is fine even if tmp goes out of scope later since it copies in the set method... ret = rcutils_hash_map_set(out, &individual_description->fields.data[i].name.data, &tmp); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR( - "Could not set hash map entry for field: %s", - individual_description->fields.data[i].name.data); - fail_ret = ret; + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not set hash map entry for field: %s:\n%s", + individual_description->fields.data[i].name.data, + error_string.str); goto fail; } } @@ -169,16 +160,14 @@ rosidl_runtime_c_type_description_utils_get_field_map( fail: { - rcutils_ret_t fini_ret = rcutils_hash_map_fini(out); - if (fini_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize hash map"); + if (rcutils_hash_map_fini(out) != RCUTILS_RET_OK) { + RCUTILS_SAFE_FWRITE_TO_STDERR("While handling another error, failed to finalize hash map"); } allocator->deallocate(out, allocator->state); } - return fail_ret; + return ret; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * referenced_types, @@ -189,13 +178,13 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( RCUTILS_CHECK_ARGUMENT_FOR_NULL(allocator, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(hash_map, RCUTILS_RET_INVALID_ARGUMENT); if (*hash_map != NULL) { - RCUTILS_LOG_ERROR("`hash_map` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'hash_map' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } rcutils_hash_map_t * out = allocator->allocate(sizeof(rcutils_hash_map_t), allocator->state); if (out == NULL) { - RCUTILS_LOG_ERROR("Could not allocate output hash map"); + RCUTILS_SET_ERROR_MSG("Could not allocate output hash map"); return RCUTILS_RET_BAD_ALLOC; } *out = rcutils_get_zero_initialized_hash_map(); @@ -210,11 +199,11 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( allocator); if (ret != RCUTILS_RET_OK) { allocator->deallocate(out, allocator->state); - RCUTILS_LOG_ERROR("Could not init hash map"); + RCUTILS_SET_ERROR_MSG("Could not init hash map"); return ret; } - for (size_t i = 0; i < referenced_types->size; i++) { + for (size_t i = 0; i < referenced_types->size; ++i) { rosidl_runtime_c__type_description__IndividualTypeDescription * tmp = &referenced_types->data[i]; @@ -224,7 +213,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( ret = rcutils_hash_map_get( out, &referenced_types->data[i].type_name.data, &stored_description); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Could not get stored description: %s", referenced_types->data[i].type_name.data); fail_ret = ret; // Most likely a RCUTILS_RET_NOT_FOUND goto fail; @@ -234,7 +223,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( &referenced_types->data[i], stored_description)) { // Non-identical duplicate referenced types is invalid (it's ambiguous which one to use) - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG( "Passed referenced IndividualTypeDescriptions has non-identical duplicate types"); fail_ret = RCUTILS_RET_INVALID_ARGUMENT; goto fail; @@ -244,7 +233,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( // Passing tmp is fine even if tmp goes out of scope later since it copies in the set method... ret = rcutils_hash_map_set(out, &referenced_types->data[i].type_name.data, &tmp); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Could not set hash map entry for referenced type: %s", referenced_types->data[i].type_name.data); fail_ret = ret; @@ -255,7 +244,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( size_t map_length; ret = rcutils_hash_map_get_size(out, &map_length); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not get size of hash map for validation"); + RCUTILS_SET_ERROR_MSG("Could not get size of hash map for validation"); fail_ret = RCUTILS_RET_ERROR; goto fail; } @@ -267,7 +256,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( { rcutils_ret_t fini_ret = rcutils_hash_map_fini(out); if (fini_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize hash map"); + RCUTILS_SAFE_FWRITE_TO_STDERR("While handling another error, failed to finalize hash map"); } allocator->deallocate(out, allocator->state); } @@ -278,7 +267,6 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( // ================================================================================================= // DESCRIPTION VALIDITY // ================================================================================================= - rcutils_ret_t rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( const rosidl_runtime_c__type_description__IndividualTypeDescription * main_type_description, @@ -302,7 +290,7 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio *seen_map = allocator->allocate(sizeof(rcutils_hash_map_t), allocator->state); if (*seen_map == NULL) { - RCUTILS_LOG_ERROR("Could not allocate hash map"); + RCUTILS_SET_ERROR_MSG("Could not allocate hash map"); return RCUTILS_RET_BAD_ALLOC; } **seen_map = rcutils_get_zero_initialized_hash_map(); @@ -311,7 +299,7 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio ret = rcutils_hash_map_get_size(referenced_types_map, &referenced_types_map_size); if (ret != RCUTILS_RET_OK) { allocator->deallocate(*seen_map, allocator->state); - RCUTILS_LOG_ERROR("Could not get size of referenced types hash map"); + RCUTILS_SET_ERROR_MSG("Could not get size of referenced types hash map"); *seen_map = NULL; return RCUTILS_RET_ERROR; } @@ -323,19 +311,19 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio allocator); if (ret != RCUTILS_RET_OK) { allocator->deallocate(*seen_map, allocator->state); - RCUTILS_LOG_ERROR("Could not init hash map"); + RCUTILS_SET_ERROR_MSG("Could not init hash map"); *seen_map = NULL; return RCUTILS_RET_BAD_ALLOC; } } // 2. Iterate through fields - for (size_t i = 0; i < main_type_description->fields.size; i++) { + for (size_t i = 0; i < main_type_description->fields.size; ++i) { rosidl_runtime_c__type_description__Field * field = &main_type_description->fields.data[i]; // 3. Skip cases // continue if field is not nested type or nested type is in seen map: if ((field->type.type_id % - ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER) != 1 || + ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_MASK) != 1 || rcutils_hash_map_key_exists(*seen_map, &field->type.nested_type_name.data)) { continue; @@ -344,13 +332,14 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio // 4. Error cases // Referenced type does not exist if (!rcutils_hash_map_key_exists(referenced_types_map, &field->type.nested_type_name.data)) { - RCUTILS_LOG_ERROR("Missing referenced type: %s", field->type.nested_type_name.data); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Missing referenced type: %s", field->type.nested_type_name.data); fail_ret = RCUTILS_RET_NOT_FOUND; goto fail; } // Nested name empty if (field->type.nested_type_name.size == 0) { - RCUTILS_LOG_ERROR("Missing referenced type name"); + RCUTILS_SET_ERROR_MSG("Missing referenced type name"); fail_ret = RCUTILS_RET_INVALID_ARGUMENT; goto fail; } @@ -361,7 +350,7 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio ret = rcutils_hash_map_get( referenced_types_map, &field->type.nested_type_name.data, &necessary_description); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Could not get necessary referenced type: %s", field->type.nested_type_name.data); fail_ret = ret; // Most likely a RCUTILS_RET_NOT_FOUND goto fail; @@ -369,7 +358,7 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio // Check for mismatched name if (strcmp(field->type.nested_type_name.data, necessary_description->type_name.data) != 0) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Necessary referenced type name (%s) did not match field's nested type name (%s)", necessary_description->type_name.data, field->type.nested_type_name.data); @@ -381,7 +370,8 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio ret = rcutils_hash_map_set( *seen_map, &field->type.nested_type_name.data, &necessary_description); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to set hash map for key: %s", field->type.nested_type_name.data); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Failed to set hash map for key: %s", field->type.nested_type_name.data); fail_ret = ret; goto fail; } @@ -390,7 +380,8 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( necessary_description, referenced_types_map, allocator, seen_map); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Recursion failed on: %s", necessary_description->type_name.data); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Recursion failed on: %s", necessary_description->type_name.data); fail_ret = ret; goto fail; } @@ -402,7 +393,7 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio if (top_level_call) { rcutils_ret_t fini_ret = rcutils_hash_map_fini(*seen_map); if (fini_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize hash map"); + RCUTILS_SAFE_FWRITE_TO_STDERR("While handling another error, failed to finalize hash map"); } allocator->deallocate(*seen_map, allocator->state); *seen_map = NULL; @@ -410,7 +401,6 @@ rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptio return fail_ret; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_descriptions_map( const rcutils_hash_map_t * hash_map, @@ -419,7 +409,7 @@ rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_ { RCUTILS_CHECK_ARGUMENT_FOR_NULL(hash_map, RCUTILS_RET_INVALID_ARGUMENT); if (*sequence != NULL) { - RCUTILS_LOG_ERROR("`sequence` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'sequence' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } @@ -427,13 +417,13 @@ rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_ rcutils_ret_t ret = RCUTILS_RET_ERROR; ret = rcutils_hash_map_get_size(hash_map, &map_length); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not get size of hash map"); + RCUTILS_SET_ERROR_MSG("Could not get size of hash map"); return RCUTILS_RET_ERROR; } *sequence = rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__create( map_length); if (*sequence == NULL) { - RCUTILS_LOG_ERROR("Could allocate sequence"); + RCUTILS_SET_ERROR_MSG("Could allocate sequence"); return RCUTILS_RET_BAD_ALLOC; } @@ -443,7 +433,7 @@ rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_ rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); while (RCUTILS_RET_OK == status) { if (strcmp(key, data->type_name.data) != 0) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Necessary referenced type name (%s) did not match key (%s)", data->type_name.data, key); rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__destroy(*sequence); return RCUTILS_RET_INVALID_ARGUMENT; @@ -453,7 +443,7 @@ rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_ if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( data, &((*sequence)->data[i]))) { - RCUTILS_LOG_ERROR("Could not copy type %s to sequence", key); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("Could not copy type %s to sequence", key); rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__destroy(*sequence); return RCUTILS_RET_BAD_ALLOC; } @@ -466,7 +456,8 @@ rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_ rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place(*sequence); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_WARN("Could not sort copy of referenced type descriptions for validation"); + RCUTILS_SET_ERROR_MSG("Could not sort copy of referenced type descriptions for validation"); + return ret; } } @@ -490,7 +481,6 @@ rosidl_runtime_c_type_description_utils_referenced_type_description_sequence_sor return strcmp(left->type_name.data, right->type_name.data); } - rcutils_ret_t rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * sequence) @@ -503,7 +493,6 @@ rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_pla rosidl_runtime_c_type_description_utils_referenced_type_description_sequence_sort_compare); } - rcutils_ret_t rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_place( const rosidl_runtime_c__type_description__IndividualTypeDescription * main_type_description, @@ -513,8 +502,6 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_types, RCUTILS_RET_INVALID_ARGUMENT); rcutils_ret_t ret = RCUTILS_RET_ERROR; - rcutils_ret_t end_ret = RCUTILS_RET_ERROR; - rcutils_ret_t fini_ret = RCUTILS_RET_ERROR; rcutils_hash_map_t * referenced_types_map = NULL; rcutils_hash_map_t * necessary_map = NULL; @@ -523,28 +510,35 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl ret = rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( referenced_types, &allocator, &referenced_types_map); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not construct referenced type description map"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not construct referenced type description map:\n%s", error_string.str); return ret; } ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( main_type_description, referenced_types_map, &allocator, &necessary_map); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not construct necessary referenced type description map"); - end_ret = ret; + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not construct necessary referenced type description map:\n%s", error_string.str); goto end_ref; } size_t map_length; ret = rcutils_hash_map_get_size(necessary_map, &map_length); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not get size of hash map for validation"); - end_ret = RCUTILS_RET_ERROR; + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not get size of hash map for validation:\n%s", error_string.str); goto end_necessary; } // End early if pruning was not needed if (referenced_types->size == map_length) { - end_ret = RCUTILS_RET_OK; + ret = RCUTILS_RET_OK; goto end_necessary; } @@ -552,11 +546,11 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl char * key; rosidl_runtime_c__type_description__IndividualTypeDescription * data = NULL; rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(necessary_map, NULL, &key, &data); - while (RCUTILS_RET_OK == status) { + while (status == RCUTILS_RET_OK) { if (strcmp(key, data->type_name.data) != 0) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Necessary referenced type name (%s) did not match key (%s)", data->type_name.data, key); - end_ret = RCUTILS_RET_ERROR; + ret = RCUTILS_RET_ERROR; goto end_necessary; } @@ -567,9 +561,9 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( data, &referenced_types->data[append_count++])) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Could not copy necessary referenced type description %s to rearrange", key); - end_ret = RCUTILS_RET_ERROR; + ret = RCUTILS_RET_ERROR; goto end_necessary; } } else { @@ -579,7 +573,7 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl } // Finalize entries after the section of necessary referenced types, and shrink the input sequence - for (size_t i = append_count; i < referenced_types->size; i++) { + for (size_t i = append_count; i < referenced_types->size; ++i) { rosidl_runtime_c__type_description__IndividualTypeDescription__fini( &referenced_types->data[i]); } @@ -589,157 +583,164 @@ rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_pl rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate(referenced_types->data, allocation_size, allocator.state); if (next_ptr == NULL && allocation_size != 0) { - RCUTILS_LOG_ERROR( - "Could not shrink the necessary referenced type descriptions sequence during rearrangement! " - "Beware! The referenced type descriptions was likely already partially modified in place!"); - end_ret = RCUTILS_RET_BAD_ALLOC; + RCUTILS_SET_ERROR_MSG( + "Could not shrink the necessary referenced type descriptions sequence during rearrangement. " + "Beware: The referenced type descriptions was likely already partially modified in place."); + ret = RCUTILS_RET_BAD_ALLOC; goto end_necessary; } referenced_types->data = next_ptr; referenced_types->size = append_count; referenced_types->capacity = append_count; - end_ret = RCUTILS_RET_OK; + ret = RCUTILS_RET_OK; end_necessary: { - fini_ret = rcutils_hash_map_fini(necessary_map); - if (fini_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize hash map"); + if (rcutils_hash_map_fini(necessary_map) != RCUTILS_RET_OK) { + RCUTILS_SAFE_FWRITE_TO_STDERR("While handling another error, failed to finalize hash map"); } allocator.deallocate(necessary_map, allocator.state); } end_ref: { - fini_ret = rcutils_hash_map_fini(referenced_types_map); - if (fini_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize hash map"); + if (rcutils_hash_map_fini(referenced_types_map) != RCUTILS_RET_OK) { + RCUTILS_SAFE_FWRITE_TO_STDERR("While handling another error, failed to finalize hash map"); } allocator.deallocate(referenced_types_map, allocator.state); } - return end_ret; + return ret; } - -bool +rcutils_ret_t rosidl_runtime_c_type_description_utils_field_is_valid( const rosidl_runtime_c__type_description__Field * field) { - if (field == NULL) { - RCUTILS_LOG_WARN("Field is invalid: Pointer is null"); - return false; - } + RCUTILS_CHECK_ARGUMENT_FOR_NULL(field, RCUTILS_RET_INVALID_ARGUMENT); + if (field->name.size == 0) { - RCUTILS_LOG_WARN("Field is invalid: Empty name"); - return false; + RCUTILS_SET_ERROR_MSG("Field is invalid: Empty name"); + return RCUTILS_RET_NOT_INITIALIZED; } - if ((field->type.type_id % ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER) == - 0) - { - RCUTILS_LOG_WARN("Field `%s` is invalid: Unset type ID", field->name.data); - return false; + if ((field->type.type_id % ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_MASK) == 0) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Field `%s` is invalid: Unset type ID", field->name.data); + return RCUTILS_RET_NOT_INITIALIZED; } - if ((field->type.type_id % ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_DELIMITER) == + if ((field->type.type_id % ROSIDL_RUNTIME_C_TYPE_DESCRIPTION_UTILS_SEQUENCE_TYPE_ID_MASK) == 1 && field->type.nested_type_name.size == 0) { - RCUTILS_LOG_WARN( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Field `%s` is invalid: Field is nested but with empty nested type name", field->name.data); - return false; + return RCUTILS_RET_NOT_INITIALIZED; } - return true; + return RCUTILS_RET_OK; } - -bool +rcutils_ret_t rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( const rosidl_runtime_c__type_description__IndividualTypeDescription * description) { - if (description == NULL) { - RCUTILS_LOG_WARN("Individual type description is invalid: Pointer is null"); - return false; - } + RCUTILS_CHECK_ARGUMENT_FOR_NULL(description, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_ret_t ret = RCUTILS_RET_ERROR; + if (description->type_name.size == 0) { - RCUTILS_LOG_WARN("Individual type description is invalid: Empty name"); - return false; + RCUTILS_SET_ERROR_MSG("Individual type description is invalid: Empty name"); + return RCUTILS_RET_NOT_INITIALIZED; } - for (size_t i = 0; i < description->fields.size; i++) { - if (!rosidl_runtime_c_type_description_utils_field_is_valid(&description->fields.data[i])) { - RCUTILS_LOG_WARN( - "Individual type description `%s` is invalid: Invalid field", description->type_name.data); - return false; + for (size_t i = 0; i < description->fields.size; ++i) { + ret = rosidl_runtime_c_type_description_utils_field_is_valid(&description->fields.data[i]); + if (ret != RCUTILS_RET_OK) { + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Individual type description `%s` is invalid: Invalid field:\n%s", + description->type_name.data, + error_string.str); + return ret; } } - bool end_ret = false; rcutils_hash_map_t * hash_map = NULL; rcutils_allocator_t allocator = rcutils_get_default_allocator(); - rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_get_field_map( + ret = rosidl_runtime_c_type_description_utils_get_field_map( description, &allocator, &hash_map); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not construct field map for validation"); - return false; + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not construct field map for validation:\n%s", error_string.str); + return ret; } size_t map_length; ret = rcutils_hash_map_get_size(hash_map, &map_length); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not get size of field map for validation"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not get size of field map for validation:\n%s", error_string.str); goto end; } if (description->fields.size != map_length) { - RCUTILS_LOG_WARN( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Individual type description `%s` is invalid: Duplicate fields", description->type_name.data); + ret = RCUTILS_RET_INVALID_ARGUMENT; goto end; } - end_ret = true; + return RCUTILS_RET_OK; end: { - rcutils_ret_t fini_ret = rcutils_hash_map_fini(hash_map); - if (fini_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize hash map"); + if (rcutils_hash_map_fini(hash_map) != RCUTILS_RET_OK) { + RCUTILS_SAFE_FWRITE_TO_STDERR("While handling another error, failed to finalize hash map"); } allocator.deallocate(hash_map, allocator.state); - return end_ret; + return ret; } } - -bool +rcutils_ret_t rosidl_runtime_c_type_description_utils_type_description_is_valid( const rosidl_runtime_c__type_description__TypeDescription * description) { - if (description == NULL) { - RCUTILS_LOG_WARN("Type description is invalid: Pointer is null"); - return false; - } + RCUTILS_CHECK_ARGUMENT_FOR_NULL(description, RCUTILS_RET_INVALID_ARGUMENT); - if (!rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( - &description->type_description)) - { + rcutils_ret_t ret = RCUTILS_RET_ERROR; + + ret = rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + &description->type_description); + if (ret != RCUTILS_RET_OK) { + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); if (description->type_description.type_name.size != 0) { - RCUTILS_LOG_WARN( - "Type description `%s` is invalid: Main type description is invalid", - description->type_description.type_name.data); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Type description `%s` is invalid: Main type description is invalid:\n%s", + description->type_description.type_name.data, + error_string.str); } else { - RCUTILS_LOG_WARN("Type description is invalid: Main type description is invalid"); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Type description is invalid: Main type description is invalid:\n%s", error_string.str); } - return false; + return ret; } - bool end_ret = false; rcutils_hash_map_t * referenced_types_map = NULL; rcutils_allocator_t allocator = rcutils_get_default_allocator(); - rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( + ret = rosidl_runtime_c_type_description_utils_get_referenced_type_description_map( &description->referenced_type_descriptions, &allocator, &referenced_types_map); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not construct referenced type description map"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not construct referenced type description map:\n%s", error_string.str); return false; } @@ -748,13 +749,18 @@ rosidl_runtime_c_type_description_utils_type_description_is_valid( // Check no duplicated ref types ret = rcutils_hash_map_get_size(referenced_types_map, &map_length); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not get size of referenced type description map for validation"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not get size of referenced type description map for validation:\n%s", + error_string.str); goto end_ref; } if (description->referenced_type_descriptions.size != map_length) { - RCUTILS_LOG_WARN( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Type description `%s` is invalid: Duplicate referenced type descriptions", description->type_description.type_name.data); + ret = RCUTILS_RET_INVALID_ARGUMENT; goto end_ref; } @@ -763,33 +769,43 @@ rosidl_runtime_c_type_description_utils_type_description_is_valid( ret = rosidl_runtime_c_type_description_utils_get_necessary_referenced_type_descriptions_map( &description->type_description, referenced_types_map, &allocator, &necessary_types_map); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not construct necessary referenced type description map"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not construct necessary referenced type description map:\n%s", error_string.str); goto end_ref; } // Check no unnecessary ref types ret = rcutils_hash_map_get_size(necessary_types_map, &map_length); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR( - "Could not get size of necessary referenced type description map for validation"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not get size of necessary referenced type description map for validation:\n%s", + error_string.str); goto end_necessary; } if (description->referenced_type_descriptions.size != map_length) { - RCUTILS_LOG_WARN( + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Type description `%s` is invalid: Unnecessary referenced type descriptions", description->type_description.type_name.data); + ret = RCUTILS_RET_INVALID_ARGUMENT; goto end_necessary; } // Check all referenced types valid (the prior checks ensure all of them are necessary) - for (size_t i = 0; i < description->referenced_type_descriptions.size; i++) { - if (!rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( - &description->referenced_type_descriptions.data[i])) - { - RCUTILS_LOG_WARN( - "Type description `%s` is invalid: Invalid referenced type description", - description->type_description.type_name.data); + for (size_t i = 0; i < description->referenced_type_descriptions.size; ++i) { + ret = rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + &description->referenced_type_descriptions.data[i]); + if (ret != RCUTILS_RET_OK) { + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Type description `%s` is invalid: Invalid referenced type description:\n%s", + description->type_description.type_name.data, + error_string.str); goto end_necessary; } } @@ -798,79 +814,102 @@ rosidl_runtime_c_type_description_utils_type_description_is_valid( rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * sorted_sequence = rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__create(map_length); if (sorted_sequence == NULL) { - RCUTILS_LOG_ERROR("Could allocate sequence for copy of referenced type descriptions"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could allocate sequence for copy of referenced type descriptions:\n%s", error_string.str); + ret = RCUTILS_RET_BAD_ALLOC; goto end_necessary; } if (!rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__copy( &description->referenced_type_descriptions, sorted_sequence)) { - RCUTILS_LOG_ERROR("Could not copy referenced type descriptions for validation"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not copy referenced type descriptions for validation:\n%s", error_string.str); + ret = RCUTILS_RET_BAD_ALLOC; goto end_sequence; } ret = rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( sorted_sequence); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not sort copy of referenced type descriptions for validation"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not sort copy of referenced type descriptions for validation:\n%s", error_string.str); goto end_sequence; } if (!rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__are_equal( &description->referenced_type_descriptions, sorted_sequence)) { - RCUTILS_LOG_WARN( - "Type description `%s` is invalid: Referenced type descriptions not sorted", - description->type_description.type_name.data); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Type description `%s` is invalid: Referenced type descriptions not sorted:\n%s", + description->type_description.type_name.data, + error_string.str); + ret = RCUTILS_RET_INVALID_ARGUMENT; goto end_sequence; } - end_ret = true; + return RCUTILS_RET_OK; end_sequence: rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__destroy(sorted_sequence); end_necessary: - ret = rcutils_hash_map_fini(necessary_types_map); - if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize referenced types map"); + if (rcutils_hash_map_fini(necessary_types_map) != RCUTILS_RET_OK) { + RCUTILS_SAFE_FWRITE_TO_STDERR( + "While handling another error, failed to finalize necessary referenced types map"); } allocator.deallocate(necessary_types_map, allocator.state); end_ref: - ret = rcutils_hash_map_fini(referenced_types_map); - if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Failed to finalize referenced types map"); + if (rcutils_hash_map_fini(referenced_types_map) != RCUTILS_RET_OK) { + RCUTILS_SAFE_FWRITE_TO_STDERR( + "While handling another error, failed to finalize referenced types map"); } allocator.deallocate(referenced_types_map, allocator.state); - return end_ret; + return ret; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( rosidl_runtime_c__type_description__TypeDescription * type_description) { - if (!rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( - &type_description->type_description)) - { - RCUTILS_LOG_ERROR("Could not make type description valid: Invalid main type description"); - return RCUTILS_RET_ERROR; + rcutils_ret_t ret = rosidl_runtime_c_type_description_utils_individual_type_description_is_valid( + &type_description->type_description); + if (ret != RCUTILS_RET_OK) { + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not make type description valid: Invalid main type description:\n%s", + error_string.str); + return ret; } - rcutils_ret_t ret; ret = rosidl_runtime_c_type_description_utils_prune_referenced_type_descriptions_in_place( &type_description->type_description, &type_description->referenced_type_descriptions); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_WARN( - "Could not make type description valid: Could not prune referenced_type_descriptions"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not make type description valid: Could not prune referenced_type_descriptions:\n%s", + error_string.str); return ret; } ret = rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( &type_description->referenced_type_descriptions); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_WARN( - "Could not make type description valid: Could not sort referenced_type_descriptions"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not make type description valid: Could not sort referenced_type_descriptions:\n%s", + error_string.str); return ret; } @@ -881,7 +920,6 @@ rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_plac // ================================================================================================= // DESCRIPTION CONSTRUCTION // ================================================================================================= - rcutils_ret_t rosidl_runtime_c_type_description_utils_create_field( const char * name, size_t name_length, @@ -894,27 +932,31 @@ rosidl_runtime_c_type_description_utils_create_field( RCUTILS_CHECK_ARGUMENT_FOR_NULL(nested_type_name, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(default_value, RCUTILS_RET_INVALID_ARGUMENT); if (*field != NULL) { - RCUTILS_LOG_ERROR("`field` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'field' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } *field = rosidl_runtime_c__type_description__Field__create(); if (*field == NULL) { - RCUTILS_LOG_ERROR("Could not create field"); + RCUTILS_SET_ERROR_MSG( + "Could not create field"); return RCUTILS_RET_BAD_ALLOC; } // Field if (!rosidl_runtime_c__String__assignn(&(*field)->name, name, name_length)) { - RCUTILS_LOG_ERROR("Could not assign field name"); - rosidl_runtime_c__type_description__Field__destroy(*field); + RCUTILS_SET_ERROR_MSG( + "Could not assign field name"); + rosidl_runtime_c__type_description__Field__destroy( + *field); *field = NULL; return RCUTILS_RET_BAD_ALLOC; } if (!rosidl_runtime_c__String__assignn( &(*field)->default_value, default_value, default_value_length)) { - RCUTILS_LOG_ERROR("Could not assign field default value"); + RCUTILS_SET_ERROR_MSG( + "Could not assign field default value"); rosidl_runtime_c__type_description__Field__destroy(*field); *field = NULL; return RCUTILS_RET_BAD_ALLOC; @@ -928,7 +970,8 @@ rosidl_runtime_c_type_description_utils_create_field( if (!rosidl_runtime_c__String__assignn( &(*field)->type.nested_type_name, nested_type_name, nested_type_name_length)) { - RCUTILS_LOG_ERROR("Could not assign field nested type name"); + RCUTILS_SET_ERROR_MSG( + "Could not assign field nested type name"); rosidl_runtime_c__type_description__Field__destroy(*field); *field = NULL; return RCUTILS_RET_BAD_ALLOC; @@ -937,7 +980,6 @@ rosidl_runtime_c_type_description_utils_create_field( return RCUTILS_RET_OK; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_create_individual_type_description( const char * type_name, size_t type_name_length, @@ -945,20 +987,22 @@ rosidl_runtime_c_type_description_utils_create_individual_type_description( { RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_name, RCUTILS_RET_INVALID_ARGUMENT); if (*individual_description != NULL) { - RCUTILS_LOG_ERROR("`individual_description` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'individual_description' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } *individual_description = rosidl_runtime_c__type_description__IndividualTypeDescription__create(); if (*individual_description == NULL) { - RCUTILS_LOG_ERROR("Could not create individual description"); + RCUTILS_SET_ERROR_MSG( + "Could not create individual description"); return RCUTILS_RET_BAD_ALLOC; } if (!rosidl_runtime_c__String__assignn( &(*individual_description)->type_name, type_name, type_name_length)) { - RCUTILS_LOG_ERROR("Could not assign individual description type name"); + RCUTILS_SET_ERROR_MSG( + "Could not assign individual description type name"); rosidl_runtime_c__type_description__IndividualTypeDescription__destroy(*individual_description); *individual_description = NULL; return RCUTILS_RET_BAD_ALLOC; @@ -966,7 +1010,6 @@ rosidl_runtime_c_type_description_utils_create_individual_type_description( return RCUTILS_RET_OK; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_create_type_description( const char * type_name, size_t type_name_length, @@ -974,20 +1017,22 @@ rosidl_runtime_c_type_description_utils_create_type_description( { RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_name, RCUTILS_RET_INVALID_ARGUMENT); if (*type_description != NULL) { - RCUTILS_LOG_ERROR("`type_description` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'type_description' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } *type_description = rosidl_runtime_c__type_description__TypeDescription__create(); if (*type_description == NULL) { - RCUTILS_LOG_ERROR("Could not create type description"); + RCUTILS_SET_ERROR_MSG( + "Could not create type description"); return RCUTILS_RET_BAD_ALLOC; } if (!rosidl_runtime_c__String__assignn( &(*type_description)->type_description.type_name, type_name, type_name_length)) { - RCUTILS_LOG_ERROR("Could not assign main individual description type name"); + RCUTILS_SET_ERROR_MSG( + "Could not assign main individual description type name"); rosidl_runtime_c__type_description__TypeDescription__destroy(*type_description); *type_description = NULL; return RCUTILS_RET_BAD_ALLOC; @@ -995,7 +1040,6 @@ rosidl_runtime_c_type_description_utils_create_type_description( return RCUTILS_RET_OK; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_append_field( rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, @@ -1005,7 +1049,7 @@ rosidl_runtime_c_type_description_utils_append_field( RCUTILS_CHECK_ARGUMENT_FOR_NULL(field, RCUTILS_RET_INVALID_ARGUMENT); rcutils_allocator_t allocator = rcutils_get_default_allocator(); - rcutils_ret_t fini_ret = RCUTILS_RET_ERROR; + rcutils_ret_t ret = RCUTILS_RET_ERROR; size_t allocation_size = ( (individual_type_description->fields.size + 1) * @@ -1015,7 +1059,8 @@ rosidl_runtime_c_type_description_utils_append_field( rosidl_runtime_c__type_description__Field * next_ptr = allocator.reallocate( individual_type_description->fields.data, allocation_size, allocator.state); if (next_ptr == NULL && allocation_size != 0) { - RCUTILS_LOG_ERROR("Could not realloc individual type description fields sequence"); + RCUTILS_SET_ERROR_MSG( + "Could not realloc individual type description fields sequence"); return RCUTILS_RET_BAD_ALLOC; } individual_type_description->fields.data = next_ptr; @@ -1023,15 +1068,18 @@ rosidl_runtime_c_type_description_utils_append_field( individual_type_description->fields.capacity += 1; if (!rosidl_runtime_c__type_description__Field__init(&next_ptr[last_index])) { - RCUTILS_LOG_ERROR("Could not init new individual type description field element"); - fini_ret = RCUTILS_RET_BAD_ALLOC; + RCUTILS_SET_ERROR_MSG( + "Could not init new individual type description field element"); + ret = RCUTILS_RET_BAD_ALLOC; goto fail; } if (!rosidl_runtime_c__type_description__Field__copy(field, &next_ptr[last_index])) { - RCUTILS_LOG_ERROR("Could not copy into new individual type description field element"); - rosidl_runtime_c__type_description__Field__fini(&next_ptr[last_index]); - fini_ret = RCUTILS_RET_ERROR; + RCUTILS_SET_ERROR_MSG( + "Could not copy into new individual type description field element"); + rosidl_runtime_c__type_description__Field__fini( + &next_ptr[last_index]); + ret = RCUTILS_RET_ERROR; goto fail; } @@ -1044,18 +1092,17 @@ rosidl_runtime_c_type_description_utils_append_field( individual_type_description->fields.size * sizeof(rosidl_runtime_c__type_description__Field), allocator.state); if (next_ptr == NULL && individual_type_description->fields.size != 0) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG( "Could not shorten individual type description fields sequence. " - "Excess memory will be UNINITIALIZED!"); + "Excess memory will be UNINITIALIZED."); } else { individual_type_description->fields.data = next_ptr; individual_type_description->fields.size -= 1; individual_type_description->fields.capacity -= 1; } - return fini_ret; + return ret; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_append_referenced_individual_type_description( rosidl_runtime_c__type_description__TypeDescription * type_description, @@ -1065,8 +1112,8 @@ rosidl_runtime_c_type_description_utils_append_referenced_individual_type_descri RCUTILS_CHECK_ARGUMENT_FOR_NULL(type_description, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_type_description, RCUTILS_RET_INVALID_ARGUMENT); + rcutils_ret_t ret = RCUTILS_RET_ERROR; rcutils_allocator_t allocator = rcutils_get_default_allocator(); - rcutils_ret_t fini_ret = RCUTILS_RET_ERROR; size_t allocation_size = ( (type_description->referenced_type_descriptions.size + 1) * @@ -1077,7 +1124,8 @@ rosidl_runtime_c_type_description_utils_append_referenced_individual_type_descri rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate( type_description->referenced_type_descriptions.data, allocation_size, allocator.state); if (next_ptr == NULL && allocation_size != 0) { - RCUTILS_LOG_ERROR("Could not realloc type description referenced type descriptions sequence"); + RCUTILS_SET_ERROR_MSG( + "Could not realloc type description referenced type descriptions sequence"); return RCUTILS_RET_BAD_ALLOC; } type_description->referenced_type_descriptions.data = next_ptr; @@ -1085,8 +1133,9 @@ rosidl_runtime_c_type_description_utils_append_referenced_individual_type_descri type_description->referenced_type_descriptions.capacity += 1; if (!rosidl_runtime_c__type_description__IndividualTypeDescription__init(&next_ptr[last_index])) { - RCUTILS_LOG_ERROR("Could not init new type description referenced type descriptions element"); - fini_ret = RCUTILS_RET_BAD_ALLOC; + RCUTILS_SET_ERROR_MSG( + "Could not init new type description referenced type descriptions element"); + ret = RCUTILS_RET_BAD_ALLOC; goto fail; } @@ -1094,19 +1143,19 @@ rosidl_runtime_c_type_description_utils_append_referenced_individual_type_descri referenced_type_description, &next_ptr[last_index])) { // Attempt to undo changes on failure - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG( "Could not copy into new type description referenced type descriptions element"); rosidl_runtime_c__type_description__IndividualTypeDescription__fini(&next_ptr[last_index]); - fini_ret = RCUTILS_RET_ERROR; + ret = RCUTILS_RET_ERROR; goto fail; } if (sort) { - rcutils_ret_t ret = - rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( + ret = rosidl_runtime_c_type_description_utils_sort_referenced_type_descriptions_in_place( &type_description->referenced_type_descriptions); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_WARN("Could not sort copy of referenced type descriptions for validation"); + RCUTILS_SET_ERROR_MSG("Could not sort copy of referenced type descriptions for validation"); + return ret; } } return RCUTILS_RET_OK; @@ -1121,18 +1170,20 @@ rosidl_runtime_c_type_description_utils_append_referenced_individual_type_descri ), allocator.state); if (next_ptr == NULL && type_description->referenced_type_descriptions.size != 0) { - RCUTILS_LOG_ERROR( + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Could not shorten type description referenced type descriptions sequence. " - "Excess memory will be UNINITIALIZED!"); + "Excess memory will be UNINITIALIZED:\n%s", + error_string.str); } else { type_description->referenced_type_descriptions.data = next_ptr; type_description->referenced_type_descriptions.size -= 1; type_description->referenced_type_descriptions.capacity -= 1; } - return fini_ret; + return ret; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_append_referenced_type_description( rosidl_runtime_c__type_description__TypeDescription * type_description, @@ -1154,15 +1205,17 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( rosidl_runtime_c__type_description__IndividualTypeDescription * next_ptr = allocator.reallocate( type_description->referenced_type_descriptions.data, allocation_size, allocator.state); if (next_ptr == NULL && allocation_size != 0) { - RCUTILS_LOG_ERROR("Could not realloc type description referenced type descriptions sequence"); + RCUTILS_SET_ERROR_MSG( + "Could not realloc type description referenced type descriptions sequence"); return RCUTILS_RET_BAD_ALLOC; } size_t init_reset_size = 0; size_t last_index = type_description->referenced_type_descriptions.size; - for (size_t i = last_index; i < last_index + extend_count; i++) { + for (size_t i = last_index; i < last_index + extend_count; ++i) { if (!rosidl_runtime_c__type_description__IndividualTypeDescription__init(&next_ptr[i])) { - RCUTILS_LOG_ERROR("Could not init new type description referenced type descriptions element"); + RCUTILS_SET_ERROR_MSG( + "Could not init new type description referenced type descriptions element"); fini_ret = RCUTILS_RET_BAD_ALLOC; goto fail; } @@ -1173,7 +1226,7 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( &type_description_to_append->type_description, &next_ptr[last_index])) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG( "Could not copy into new type description referenced type descriptions element"); fini_ret = RCUTILS_RET_ERROR; goto fail; @@ -1181,12 +1234,13 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( // Copy type_description_to_append's referenced type descriptions // There are (extend_count - 1) referenced type descriptions to copy - for (size_t i = last_index + 1; i < last_index + extend_count; i++) { + for (size_t i = last_index + 1; i < last_index + extend_count; ++i) { if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( &type_description_to_append->referenced_type_descriptions.data[i - 1 - last_index], &next_ptr[i])) { - RCUTILS_LOG_ERROR("Could not copy new type description referenced type descriptions element"); + RCUTILS_SET_ERROR_MSG( + "Could not copy new type description referenced type descriptions element"); fini_ret = RCUTILS_RET_BAD_ALLOC; goto fail; } @@ -1202,7 +1256,10 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( type_description); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_WARN("Could not coerce type description to valid!"); + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not coerce type description to valid:\n%s", error_string.str); return RCUTILS_RET_WARN; } } @@ -1222,16 +1279,15 @@ rosidl_runtime_c_type_description_utils_append_referenced_type_description( ), allocator.state); if (next_ptr == NULL && type_description->referenced_type_descriptions.size != 0) { - RCUTILS_LOG_ERROR( + RCUTILS_SET_ERROR_MSG( "Could not shorten type description referenced type descriptions sequence. " - "Excess memory will be UNINITIALIZED!"); + "Excess memory will be UNINITIALIZED."); type_description->referenced_type_descriptions.size += extend_count; type_description->referenced_type_descriptions.capacity += extend_count; } return fini_ret; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence * @@ -1243,20 +1299,20 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_descriptions, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(referenced_description, RCUTILS_RET_INVALID_ARGUMENT); if (*output_description != NULL) { - RCUTILS_LOG_ERROR("`output_description` output argument is not pointing to null"); + RCUTILS_SET_ERROR_MSG("'output_description' output argument is not pointing to null"); return RCUTILS_RET_INVALID_ARGUMENT; } *output_description = rosidl_runtime_c__type_description__TypeDescription__create(); if (*output_description == NULL) { - RCUTILS_LOG_ERROR("Could not create output type description"); + RCUTILS_SET_ERROR_MSG("Could not create output type description"); return RCUTILS_RET_BAD_ALLOC; } if (!rosidl_runtime_c__type_description__IndividualTypeDescription__copy( referenced_description, &(*output_description)->type_description)) { - RCUTILS_LOG_ERROR("Could not copy referenced type description into main description"); + RCUTILS_SET_ERROR_MSG("Could not copy referenced type description into main description"); rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); *output_description = NULL; return RCUTILS_RET_ERROR; @@ -1265,7 +1321,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ if (!rosidl_runtime_c__type_description__IndividualTypeDescription__Sequence__copy( referenced_descriptions, &(*output_description)->referenced_type_descriptions)) { - RCUTILS_LOG_ERROR("Could not copy referenced type descriptions"); + RCUTILS_SET_ERROR_MSG("Could not copy referenced type descriptions"); rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); *output_description = NULL; return RCUTILS_RET_ERROR; @@ -1276,7 +1332,7 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ rosidl_runtime_c_type_description_utils_coerce_to_valid_type_description_in_place( *output_description); if (ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not coerce output type description to valid"); + RCUTILS_SET_ERROR_MSG("Could not coerce output type description to valid"); rosidl_runtime_c__type_description__TypeDescription__destroy(*output_description); *output_description = NULL; return ret; @@ -1285,7 +1341,6 @@ rosidl_runtime_c_type_description_utils_get_referenced_type_description_as_type_ return RCUTILS_RET_OK; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description, @@ -1304,21 +1359,21 @@ rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_na repl = rcutils_repl_str( individual_type_description->type_name.data, from, to, &allocator); if (repl == NULL) { - RCUTILS_LOG_ERROR("Could not replace individual type description type name"); + RCUTILS_SET_ERROR_MSG("Could not replace individual type description type name"); return RCUTILS_RET_ERROR; } ret = rosidl_runtime_c__String__assign(&(individual_type_description->type_name), repl); allocator.deallocate(repl, allocator.state); if (!ret) { - RCUTILS_LOG_ERROR("Could not assign individual type description type name"); + RCUTILS_SET_ERROR_MSG("Could not assign individual type description type name"); return RCUTILS_RET_ERROR; } // Replace field nested type names rosidl_runtime_c__type_description__Field * field = NULL; if (individual_type_description->fields.data) { - for (size_t i = 0; i < individual_type_description->fields.size; i++) { + for (size_t i = 0; i < individual_type_description->fields.size; ++i) { field = &individual_type_description->fields.data[i]; if (!field->type.nested_type_name.size) { continue; @@ -1326,18 +1381,18 @@ rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_na repl = rcutils_repl_str(field->type.nested_type_name.data, from, to, &allocator); if (repl == NULL) { - RCUTILS_LOG_ERROR( - "Could not replace individual type description field nested type name. Beware! " - "Partial in-place replacements might have already happened!"); + RCUTILS_SET_ERROR_MSG( + "Could not replace individual type description field nested type name. Beware: " + "Partial in-place replacements might have already happened."); return RCUTILS_RET_ERROR; } ret = rosidl_runtime_c__String__assign(&(field->type.nested_type_name), repl); allocator.deallocate(repl, allocator.state); if (!ret) { - RCUTILS_LOG_ERROR( - "Could not assign individual type description field nested type name. Beware! " - "Partial in-place replacements might have already happened!"); + RCUTILS_SET_ERROR_MSG( + "Could not assign individual type description field nested type name. Beware: " + "Partial in-place replacements might have already happened."); return RCUTILS_RET_ERROR; } } @@ -1345,7 +1400,6 @@ rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_na return RCUTILS_RET_OK; } - rcutils_ret_t rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_place( rosidl_runtime_c__type_description__TypeDescription * type_description, @@ -1356,32 +1410,38 @@ rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_ RCUTILS_CHECK_ARGUMENT_FOR_NULL(from, RCUTILS_RET_INVALID_ARGUMENT); RCUTILS_CHECK_ARGUMENT_FOR_NULL(to, RCUTILS_RET_INVALID_ARGUMENT); - rcutils_ret_t end_ret = RCUTILS_RET_ERROR; + rcutils_ret_t ret = RCUTILS_RET_ERROR; - end_ret = - rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( + /* *INDENT-OFF* */ // Uncrustify will dislodge the NOLINT + ret = rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( // NOLINT &type_description->type_description, from, to); - if (end_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR("Could not replace main type description type name"); - return end_ret; + /* *INDENT-ON* */ + if (ret != RCUTILS_RET_OK) { + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not replace main type description type name:\n%s", error_string.str); + return ret; } if (type_description->referenced_type_descriptions.data) { - for (size_t i = 0; i < type_description->referenced_type_descriptions.size; i++) { + for (size_t i = 0; i < type_description->referenced_type_descriptions.size; ++i) { rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description = &type_description->referenced_type_descriptions.data[i]; /* *INDENT-OFF* */ // Uncrustify will dislodge the NOLINT - end_ret = - rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( // NOLINT - individual_type_description, from, to); + ret = rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_names_in_place( // NOLINT + individual_type_description, from, to); /* *INDENT-ON* */ - if (end_ret != RCUTILS_RET_OK) { - RCUTILS_LOG_ERROR( - "Could not replace type names in referenced type. Beware! " - "Partial in-place replacements might have already happened!"); - return end_ret; + if (ret != RCUTILS_RET_OK) { + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Could not replace type names in referenced type. Beware: " + "Partial in-place replacements might have already happened:\n%s", + error_string.str); + return ret; } } } @@ -1392,7 +1452,6 @@ rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_ // ================================================================================================= // DESCRIPTION PRINTING // ================================================================================================= - void rosidl_runtime_c_type_description_utils_print_field_type( const rosidl_runtime_c__type_description__FieldType * field_type) @@ -1411,7 +1470,6 @@ rosidl_runtime_c_type_description_utils_print_field_type( } } - void rosidl_runtime_c_type_description_utils_print_field( const rosidl_runtime_c__type_description__Field * field) @@ -1433,7 +1491,6 @@ rosidl_runtime_c_type_description_utils_print_field( rosidl_runtime_c_type_description_utils_print_field_type(&field->type); } - void rosidl_runtime_c_type_description_utils_print_individual_type_description( const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description) @@ -1447,7 +1504,7 @@ rosidl_runtime_c_type_description_utils_print_individual_type_description( printf(" type_name: \"%s\"\n", individual_type_description->type_name.data); } - for (size_t i = 0; i < individual_type_description->fields.size; i++) { + for (size_t i = 0; i < individual_type_description->fields.size; ++i) { rosidl_runtime_c_type_description_utils_print_field( &individual_type_description->fields.data[i]); } @@ -1468,7 +1525,7 @@ void rosidl_runtime_c_type_description_utils_print_type_description( &type_description->type_description); printf("\n== [REFERENCED DESCRIPTIONS] ==\n"); - for (size_t i = 0; i < type_description->referenced_type_descriptions.size; i++) { + for (size_t i = 0; i < type_description->referenced_type_descriptions.size; ++i) { rosidl_runtime_c_type_description_utils_print_individual_type_description( &type_description->referenced_type_descriptions.data[i]); } @@ -1476,7 +1533,6 @@ void rosidl_runtime_c_type_description_utils_print_type_description( printf("\n---\n\n"); } - void rosidl_runtime_c_type_description_utils_print_field_map(const rcutils_hash_map_t * hash_map) { @@ -1494,7 +1550,6 @@ rosidl_runtime_c_type_description_utils_print_field_map(const rcutils_hash_map_t } } - void rosidl_runtime_c_type_description_utils_print_referenced_type_description_map( const rcutils_hash_map_t * hash_map) diff --git a/rosidl_runtime_c/test/test_type_description_utils.cpp b/rosidl_runtime_c/test/test_type_description_utils.cpp index 184df7388..3df2635d7 100644 --- a/rosidl_runtime_c/test/test_type_description_utils.cpp +++ b/rosidl_runtime_c/test/test_type_description_utils.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -325,6 +326,7 @@ TEST_F(TestUtilsFixture, test_appends_and_advanced_construction) rosidl_runtime_c_type_description_utils_append_referenced_type_description( type_description_3, type_description_1, true), RCUTILS_RET_WARN); + rcutils_reset_error(); EXPECT_EQ(type_description_3->referenced_type_descriptions.size, 4); EXPECT_EQ(type_description_3->referenced_type_descriptions.capacity, 4); @@ -365,7 +367,9 @@ TEST_F(TestUtilsFixture, test_appends_and_advanced_construction) individual_desc_1, // Depends on ref: "empty" &subset_desc_2, true); // Coercion to valid - EXPECT_TRUE(rosidl_runtime_c_type_description_utils_type_description_is_valid(subset_desc_2)); + EXPECT_EQ( + rosidl_runtime_c_type_description_utils_type_description_is_valid(subset_desc_2), + RCUTILS_RET_OK); EXPECT_EQ(subset_desc_2->referenced_type_descriptions.size, 1); EXPECT_EQ(subset_desc_2->referenced_type_descriptions.capacity, 1); EXPECT_FALSE( @@ -402,6 +406,7 @@ TEST_F(TestUtilsFixture, test_find) &individual_desc_1->fields, "a", &find_field); EXPECT_EQ(ret, RCUTILS_RET_NOT_FOUND); + rcutils_reset_error(); EXPECT_FALSE(find_field); ret = rosidl_runtime_c_type_description_utils_find_field( @@ -425,6 +430,7 @@ TEST_F(TestUtilsFixture, test_find) ret = rosidl_runtime_c_type_description_utils_find_referenced_type_description( &type_description_1->referenced_type_descriptions, "a", &find_desc); EXPECT_EQ(ret, RCUTILS_RET_NOT_FOUND); + rcutils_reset_error(); EXPECT_FALSE(find_desc); ret = rosidl_runtime_c_type_description_utils_find_referenced_type_description( From 0b87cd0b2599488efeca4f2c32bc43e766fa78b0 Mon Sep 17 00:00:00 2001 From: methylDragon Date: Fri, 7 Apr 2023 15:14:59 -0700 Subject: [PATCH 10/11] Fix windows warnings Signed-off-by: methylDragon --- rosidl_runtime_c/src/type_description_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index 0da7b0d44..a0f158f8d 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -39,7 +39,7 @@ size_t next_power_of_two(size_t v) size_t shf = 0; v--; for (size_t i = 0; shf < sizeof(size_t) * 4; ++i) { - shf = (1 << i); + shf = (((size_t) 1) << i); v |= v >> shf; } v++; From b2d7485f66a2ca216e4826c33f852d9a915adfe6 Mon Sep 17 00:00:00 2001 From: methylDragon Date: Fri, 7 Apr 2023 15:47:54 -0700 Subject: [PATCH 11/11] Remove print functions Signed-off-by: methylDragon --- .../rosidl_runtime_c/type_description_utils.h | 42 ------ rosidl_runtime_c/src/type_description_utils.c | 121 ------------------ .../test/test_type_description_utils.cpp | 1 - 3 files changed, 164 deletions(-) diff --git a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h index ae92e080e..b190a9330 100644 --- a/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h +++ b/rosidl_runtime_c/include/rosidl_runtime_c/type_description_utils.h @@ -634,7 +634,6 @@ rosidl_runtime_c_type_description_utils_repl_individual_type_description_type_na const char * from, const char * to); - /// In-place replace substrings across all type names (and references to those names) /** * This means all `IndividualTypeDescription` type_names will get replaced, in the main description @@ -650,47 +649,6 @@ rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_ const char * from, const char * to); - -// ================================================================================================= -// DESCRIPTION PRINTING -// ================================================================================================= - -/// Print rosidl_runtime_c__type_description__FieldType -ROSIDL_GENERATOR_C_PUBLIC -void -rosidl_runtime_c_type_description_utils_print_field_type( - const rosidl_runtime_c__type_description__FieldType * field_type); - -/// Print rosidl_runtime_c__type_description__Field -ROSIDL_GENERATOR_C_PUBLIC -void -rosidl_runtime_c_type_description_utils_print_field( - const rosidl_runtime_c__type_description__Field * field); - -/// Print rosidl_runtime_c__type_description__IndividualTypeDescription -ROSIDL_GENERATOR_C_PUBLIC -void -rosidl_runtime_c_type_description_utils_print_individual_type_description( - const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description -); - -/// Print rosidl_runtime_c__type_description__TypeDescription -ROSIDL_GENERATOR_C_PUBLIC -void rosidl_runtime_c_type_description_utils_print_type_description( - const rosidl_runtime_c__type_description__TypeDescription * type_description); - -/// Print hashmap of rosidl_runtime_c__type_description__Field, keyed by name -ROSIDL_GENERATOR_C_PUBLIC -void -rosidl_runtime_c_type_description_utils_print_field_map(const rcutils_hash_map_t * hash_map); - -/// Print hashmap of rosidl_runtime_c__type_description__IndividualTypeDescription, keyed by name -ROSIDL_GENERATOR_C_PUBLIC -void -rosidl_runtime_c_type_description_utils_print_referenced_type_description_map( - const rcutils_hash_map_t * hash_map); - - #ifdef __cplusplus } #endif diff --git a/rosidl_runtime_c/src/type_description_utils.c b/rosidl_runtime_c/src/type_description_utils.c index a0f158f8d..4e620f119 100644 --- a/rosidl_runtime_c/src/type_description_utils.c +++ b/rosidl_runtime_c/src/type_description_utils.c @@ -464,7 +464,6 @@ rosidl_runtime_c_type_description_utils_copy_init_sequence_from_referenced_type_ return RCUTILS_RET_OK; } - int rosidl_runtime_c_type_description_utils_referenced_type_description_sequence_sort_compare( const void * lhs, const void * rhs) @@ -1447,123 +1446,3 @@ rosidl_runtime_c_type_description_utils_repl_all_type_description_type_names_in_ } return RCUTILS_RET_OK; } - - -// ================================================================================================= -// DESCRIPTION PRINTING -// ================================================================================================= -void -rosidl_runtime_c_type_description_utils_print_field_type( - const rosidl_runtime_c__type_description__FieldType * field_type) -{ - printf( - " [FIELD TYPE]\n" - " type_id: %d\n" - " capacity: %" PRIu64 "\n" - " string_capacity: %" PRIu64 "\n", - field_type->type_id, field_type->capacity, field_type->string_capacity); - - if (field_type->nested_type_name.data == NULL) { - printf(" nested_type_name: (null)\n"); - } else { - printf(" nested_type_name: \"%s\"\n", field_type->nested_type_name.data); - } -} - -void -rosidl_runtime_c_type_description_utils_print_field( - const rosidl_runtime_c__type_description__Field * field) -{ - printf("[FIELD]\n"); - - if (field->name.data == NULL) { - printf(" name: (null)\n"); - } else { - printf(" name: \"%s\"\n", field->name.data); - } - - if (field->default_value.data == NULL) { - printf(" default_value: (null)\n"); - } else { - printf(" default_value: \"%s\"\n", field->default_value.data); - } - - rosidl_runtime_c_type_description_utils_print_field_type(&field->type); -} - -void -rosidl_runtime_c_type_description_utils_print_individual_type_description( - const rosidl_runtime_c__type_description__IndividualTypeDescription * individual_type_description) -{ - printf( - "\n[INDIVIDUAL TYPE DESCRIPTION] (Fields: %zd)\n", individual_type_description->fields.size); - - if (individual_type_description->type_name.data == NULL) { - printf(" type_name: (null)\n"); - } else { - printf(" type_name: \"%s\"\n", individual_type_description->type_name.data); - } - - for (size_t i = 0; i < individual_type_description->fields.size; ++i) { - rosidl_runtime_c_type_description_utils_print_field( - &individual_type_description->fields.data[i]); - } -} - - -void rosidl_runtime_c_type_description_utils_print_type_description( - const rosidl_runtime_c__type_description__TypeDescription * type_description) -{ - printf("\n\n---\n\n"); - - printf( - "= [PRINTING TYPE DESCRIPTION] = (Referenced descriptions: %zd)\n", - type_description->referenced_type_descriptions.size); - - printf("\n== [MAIN DESCRIPTION] ==\n"); - rosidl_runtime_c_type_description_utils_print_individual_type_description( - &type_description->type_description); - - printf("\n== [REFERENCED DESCRIPTIONS] ==\n"); - for (size_t i = 0; i < type_description->referenced_type_descriptions.size; ++i) { - rosidl_runtime_c_type_description_utils_print_individual_type_description( - &type_description->referenced_type_descriptions.data[i]); - } - - printf("\n---\n\n"); -} - -void -rosidl_runtime_c_type_description_utils_print_field_map(const rcutils_hash_map_t * hash_map) -{ - char * key; - rosidl_runtime_c__type_description__Field * data = NULL; - rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); - while (RCUTILS_RET_OK == status) { - if (key == NULL) { - printf("\n== KEY: (null) ==\n"); - } else { - printf("\n== KEY: \"%s\" ==\n", key); - } - rosidl_runtime_c_type_description_utils_print_field(data); - status = rcutils_hash_map_get_next_key_and_data(hash_map, &key, &key, &data); - } -} - -void -rosidl_runtime_c_type_description_utils_print_referenced_type_description_map( - const rcutils_hash_map_t * hash_map) -{ - char * key; - rosidl_runtime_c__type_description__IndividualTypeDescription * data; - rcutils_ret_t status = rcutils_hash_map_get_next_key_and_data(hash_map, NULL, &key, &data); - while (RCUTILS_RET_OK == status) { - if (key == NULL) { - printf("\n== KEY: (null) ==\n"); - } else { - printf("\n== KEY: \"%s\" ==\n", key); - } - rosidl_runtime_c_type_description_utils_print_individual_type_description(data); - status = rcutils_hash_map_get_next_key_and_data(hash_map, &key, &key, &data); - } -} diff --git a/rosidl_runtime_c/test/test_type_description_utils.cpp b/rosidl_runtime_c/test/test_type_description_utils.cpp index 3df2635d7..101059cfd 100644 --- a/rosidl_runtime_c/test/test_type_description_utils.cpp +++ b/rosidl_runtime_c/test/test_type_description_utils.cpp @@ -569,5 +569,4 @@ TEST_F(TestUtilsFixture, test_maps) } // TODO(methylDragon) -// TEST(Utils, test_prints) <-- deferred because it's utility // TEST(Utils, test_validity) <-- This is technically implicitly tested with appends with coercion