Skip to content

Conversation

@greg7mdp
Copy link
Contributor

@greg7mdp greg7mdp commented Apr 24, 2025

Add missing check for MAX_NUM_ARRAY_ELEMENTS in the std::array versions.
Also c++20 syntax update.

Resolves #1422.

@greg7mdp greg7mdp requested review from heifner and spoonincode April 24, 2025 19:59
Copy link
Contributor

@spoonincode spoonincode left a comment

Choose a reason for hiding this comment

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

It might be nice, since you're touching them anyways, to update these to c++20 syntax. but obviously not critical and can go as-is

@greg7mdp
Copy link
Contributor Author

It might be nice, since you're touching them anyways, to update these to c++20 syntax. but obviously not critical and can go as-is

Hopefully this is what you were thinking of.

…riate.

- pointers are `std::trivially_copyable`
- we may have a type for which FC_REFLECT was defined, which we serialize without alignment padding
  between members. But that type can also be `std::trivially_copyable`, and we wouldn't want to
  serialize it with padding when in a `std::array`.
@greg7mdp
Copy link
Contributor Author

Upon reflection, I don't think std::is_trivially_copyable is appropriate.

  • pointers are std::trivially_copyable. We wouldn't want to allow serializing an std::array of pointers.
  • we may have a type for which FC_REFLECT was defined, which we serialize without alignment padding
    between members. But that type can also be std::trivially_copyable, and we wouldn't want to
    serialize it with padding when in a std::array I think.

@greg7mdp greg7mdp changed the title Use std::is_trivially_copyable_v trait for array serialization in raw.hpp add missing check for MAX_NUM_ARRAY_ELEMENTS in the std::array pack/unpack. Also c++20 syntax update. Apr 26, 2025
@greg7mdp
Copy link
Contributor Author

@heifner @spoonincode I made some changes in this PR (realized std::is_trivially_copyable was not an appropriate replacement), and updated the PR and issue descriptions. Maybe you guys would like to have another look?

@heifner
Copy link
Contributor

heifner commented Apr 28, 2025

My understanding is std::array can have padding because it is a struct. However we don't serialize the std::array we serialize the internal c-array of the std::array. So padding is not an issue.

@spoonincode
Copy link
Contributor

My understanding is std::array can have padding because it is a struct. However we don't serialize the std::array we serialize the internal c-array of the std::array. So padding is not an issue.

I think the problem is if someone has

struct foo {
   uint32_t bar;
   uint64_t baz;
};
FC_REFLECT(foo, (bar)(baz));

This is a trivially copyable type (std::is_trivially_copyable), but has padding. So if someone makes an array<foo, 2> we can't just write out the bytes of the entire array (like early change in this PR would have done).

I wonder how we could detect that something like sha256 is safe to handle in this manner.

@greg7mdp
Copy link
Contributor Author

My understanding is std::array can have padding because it is a struct. However we don't serialize the std::array we serialize the internal c-array of the std::array. So padding is not an issue.

Hum, that's not exactly what I meant. Consider struct key_weight, which has these members:

struct key_weight {
public_key_type key;
weight_type weight;

A std::array<key_weight> will have space paddings between the key_weight values, since weight_type is an uint16_t.
But it may be std::is_trivially_copyable, so we would write the memory representation of the array, spaces and all.

So that would be a consensus change from what we were writing before, as key_weight would not have been is_trivial_array, and we would have serialized the values individually without the padding between values.

Same issue if the members were in the opposite order, weight before key. We'd have internal padding between the two members, which would be written if we just write the memory representation of the std::array, but not if we write the values separately using:

FC_REFLECT(eosio::chain::key_weight, (key)(weight) )

@greg7mdp
Copy link
Contributor Author

greg7mdp commented Apr 28, 2025

I wonder how we could detect that something like sha256 is safe to handle in this manner.

I had the exact same thought! We could detect it if sha256 used a std::array (a std::array of std::is_scalar type would be OK), but since it is using a c-style array (which decays as pointer)

I don't think we can safely match it, but then I didn't quite get what this does:

FC_REFLECT_TYPENAME( fc::sha256 )

@heifner
Copy link
Contributor

heifner commented Apr 28, 2025

Thanks guys. I did misunderstand the issue at hand.

I don't think we can safely match it, but then I didn't quite get what this does:

FC_REFLECT_TYPENAME( fc::sha256 )

That just flags that the type works with reflection. It specifies the operator<< & operator>> manually instead of using FC_REFLECT.

@greg7mdp
Copy link
Contributor Author

That just flags that the type works with reflection. It specifies the operator<< & operator>> manually instead of using FC_REFLECT.

Oh, interesting, so it does write the four uint64_t in one shot. nice!

@greg7mdp greg7mdp merged commit 5862e43 into main Apr 28, 2025
36 checks passed
@greg7mdp greg7mdp deleted the gh_1422 branch April 28, 2025 17:11
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.

raw.hpp: missing check for MAX_NUM_ARRAY_ELEMENTS in the std::array pack/unpack versions.

4 participants