Skip to content

feat: Introduce serializer "plugin" interface and null_serializer#129

Draft
NEOatNHNG wants to merge 10 commits into
eclipse-score:mainfrom
etas-contrib:NEOatNHNG/integrate-serializer
Draft

feat: Introduce serializer "plugin" interface and null_serializer#129
NEOatNHNG wants to merge 10 commits into
eclipse-score:mainfrom
etas-contrib:NEOatNHNG/integrate-serializer

Conversation

@NEOatNHNG
Copy link
Copy Markdown
Contributor

Improvement

Description

Add serializer interface and a null_serializer (which can handle pre-serialized messages) to the gatewayd.

Related ticket

closes #10

@github-actions
Copy link
Copy Markdown

The created documentation from the pull request is available at: docu-html

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 22, 2026

License Check Results

🚀 The license check job ran with the Bazel command:

bazel run //:license-check

Status: ⚠️ Needs Review

Click to expand output
[License Check Output]
Extracting Bazel installation...
Starting local Bazel server (8.3.0) and connecting to it...
INFO: Invocation ID: 241f3dfe-c48e-4820-a4c0-27d6483fa7a9
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Loading: 
Loading: 4 packages loaded
Loading: 4 packages loaded
    currently loading: 
Analyzing: target //:license-check (5 packages loaded, 0 targets configured)
Analyzing: target //:license-check (5 packages loaded, 0 targets configured)

Analyzing: target //:license-check (76 packages loaded, 10 targets configured)

Analyzing: target //:license-check (94 packages loaded, 10 targets configured)

Analyzing: target //:license-check (144 packages loaded, 2348 targets configured)

Analyzing: target //:license-check (165 packages loaded, 8772 targets configured)

Analyzing: target //:license-check (171 packages loaded, 9701 targets configured)

INFO: Analyzed target //:license-check (174 packages loaded, 11717 targets configured).
[9 / 13] [Prepa] Generating Dash formatted dependency file ... ... (2 actions, 0 running)
INFO: From Generating Dash formatted dependency file ...:
INFO: Successfully converted 2 packages from Cargo.lock to bazel-out/k8-fastbuild/bin/formatted.txt
INFO: Found 1 target...
Target //:license.check.license_check up-to-date:
  bazel-bin/license.check.license_check
  bazel-bin/license.check.license_check.jar
INFO: Elapsed time: 19.856s, Critical Path: 0.28s
INFO: 13 processes: 4 disk cache hit, 9 internal.
INFO: Build completed successfully, 13 total actions
INFO: Running command line: bazel-bin/license.check.license_check ./formatted.txt <args omitted>
usage: org.eclipse.dash.licenses.cli.Main [-batch <int>] [-cd <url>]
       [-confidence <int>] [-ef <url>] [-excludeSources <sources>] [-help] [-lic
       <url>] [-project <shortname>] [-repo <url>] [-review] [-summary <file>]
       [-timeout <seconds>] [-token <token>]

@NEOatNHNG NEOatNHNG changed the title NEOatNHNG/integrate serializer feat: Introduce serializer "plugin" interface and null_serializer May 22, 2026
Copy link
Copy Markdown
Contributor

@mariuswbr mariuswbr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had an initial look over the changes.


union SerializationConfig {
NullSerializerConfig,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the idea is then for an actual serializer configuration to add it to the union?
Shouldn't we define the table for this already as well? Or would that be done with the actual serializer implementation?
Maybe leave a comment/hint then?

Comment on lines +33 to +49
cc_library(
name = "null_serializer_impl",
srcs = ["null_serializer.cpp"],
deps = [
":interface",
":pre_serialized_data",
"//src/config:config_flatbuffers",
],
)

# Serializer that can only handle pre-serialized data and is used as default serializer for gatewayd if no other serializer is provided.
cc_shared_library(
name = "null_serializer",
shared_lib_name = "score_com_serializer.so",
visibility = ["//visibility:public"],
deps = [":null_serializer_impl"],
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this just be combined to a single cc_shared_library?

I'm becoming less and less of a fan of too many bazel targets. It gets annoying quickly if you have to list too many deps

Comment on lines +63 to +74
/// Retrieves the maximum serialized size of the serializer.
/// @param serializer Pointer to the serializer
std::size_t score_com_serializer_get_max_serialized_size(
const struct score_com_serializer* serializer);
/// Retrieves the sizeof() of the C++ data type that the serializer handles.
/// @param serializer Pointer to the serializer
/// @retval 0 if not specified by the serializer. Deserialization might not work.
std::size_t score_com_serializer_get_sizeof_object(const struct score_com_serializer* serializer);
/// Retrieves the alignof() of the C++ data type that the serializer handles.
/// @param serializer Pointer to the serializer
/// @retval 0 if not specified by the serializer. Deserialization might not work.
std::size_t score_com_serializer_get_alignof_object(const struct score_com_serializer* serializer);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an empty line in between the functions and the comments for the next function?


/// Retrieves a serializer for the specified interface and component.
/// @param service_type Identifies the interface for which the serializer is requested.
/// @param service_type_size Size of the `service_type` string in bytes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including or excluding null termination? Same thing for the element_name_size.

const struct score_com_serializer* serializer, const uint8_t* buffer, size_t buffer_size,
void* object);

/// Retrieves the maximum serialized size of the serializer.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds a bit weird. Could be mis-read?
Maybe: "Retrieves the maximum serialized size from the serializer."
Or: "Retrieves the maximum size of the C++ data type after serialization that the serializer handles."

/// Initializes the serializer plugin. This function must be called once before any calls to
/// score_com_serializer_get(). Not thread-safe.
/// @param serializer_identifier Pointer to a string that identifies the serializer plugin.
/// @param serializer_identifier_size Size of the serializer identifier string in bytes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including or excluding null termination?

if (buffer == nullptr || object == nullptr) {
return score_com_serializer_result_general_failure;
}
const auto* pre_serialized_data = static_cast<const PreSerializedData<0>*>(object);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took me a minute to understand why the cast to PreSerializedData with MaxMessageSize = 0.
Can we add something like using PreSerializedDataView = PreSerializedData<0>; and give some explanation in pre_serialized_data.h?
Explanation maybe: "View alias for accessing PreSerializedData fields when MaxMessageSize is unknown at compile time."

Or do you think it's clear enough?

Copy link
Copy Markdown
Contributor

@lurtz lurtz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would appreciate a markdown or rst file explaining the high level picture in up to 10 sentences.

Comment thread src/gatewayd/main.cpp
Comment on lines +210 to +212
if (score_com_serializer_deinit() != score_com_serializer_result_ok) {
std::cerr << "Warning: Failed to deinitialize serializer plugin." << std::endl;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are doing C++ and can make sure via destructors cleanup is always done. socom already has a Final_action class, which you may use. Or there is even something in baselibs

Comment on lines +378 to +380
assert(pre_serialized_response_sample->size == sizeof(EchoResponseTiny));
auto* response_sample =
reinterpret_cast<const EchoResponseTiny*>(pre_serialized_response_sample->data);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this pattern repeated a few times. Does it make sense to extract it into a function? Then there is only one place which asserts and reinterpret_casts.

Comment on lines +16 to +17
#include <cstddef> // Required for size_t
#include <cstdint>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you want a C interface you should also use proper C headers at the includes

#endif

/// Result type for the serializer functions
enum [[nodiscard]] score_com_serializer_result {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is [[attr]] understood by C parsers?

/// @param serializer Pointer to the serializer
std::size_t score_com_serializer_get_max_serialized_size(
const struct score_com_serializer* serializer);
/// Retrieves the sizeof() of the C++ data type that the serializer handles.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the size of the internal buffer? Or is this the payload size into which the object gets serialized into? I guess the comment needs refinement

/// Type definition for pre-serialized data which is used by an application to provide
/// pre-serialized data to gatewayd. The serializer can then be skipped.
template <std::size_t MaxMessageSize>
struct PreSerializedData {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused. How does the serializer know it is dealing with an object of type PreSerializedData? I also thought that the NullSerializer would be used in this case.

Comment on lines +200 to +203
score_com_serializer_result score_com_serializer_get(
const char* service_type, size_t service_type_size,
enum score_com_serializer_element_type element_type, const char* element_name,
size_t element_name_size, const struct score_com_serializer** serializer) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that only one serializer type per gatewayd is possible? If yes that is a design decision which should be documented, if it is not already.

}

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
*serializer = reinterpret_cast<const struct score_com_serializer*>(serializer_config);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make more sense to write this

typedef NullSerializerConfig score_com_serializer;

Then you do not need to reinterpret_cast. However I assume this might cause havoc if we decide to have different serializer types running in the same process.


EXPECT_EQ(result, score_com_serializer_result_ok);
EXPECT_EQ(written_bytes, sizeof(test_data));
EXPECT_EQ(std::memcmp(buffer.data(), test_data, sizeof(test_data)), 0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is std::equal easier to understand than memcmp?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improvement: Payload Transformation

3 participants