Skip to content

Commit 8485352

Browse files
committed
Handle trivially serializable types which can be read and written as raw bytes
1 parent c5adebf commit 8485352

File tree

8 files changed

+495
-77
lines changed

8 files changed

+495
-77
lines changed

src/sst/core/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ nobase_dist_sst_HEADERS = \
112112
serialization/impl/packer.h \
113113
serialization/impl/sizer.h \
114114
serialization/impl/serialize_string.h \
115+
serialization/impl/serialize_trivial.h \
115116
serialization/impl/serialize_valarray.h \
116117
serialization/impl/unpacker.h \
117118
serialization/serializer.h \
@@ -228,6 +229,7 @@ sst_core_sources = \
228229
serialization/statics.cc \
229230
serialization/impl/mapper.cc \
230231
serialization/impl/serialize_array.cc \
232+
serialization/impl/serialize_trivial.cc \
231233
sstinfo.h \
232234
interfaces/TestEvent.cc \
233235
interfaces/stdMem.cc \

src/sst/core/serialization/impl/serialize_array.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
#include "sst/core/serialization/serialize.h"
1515

16+
#include <cstddef>
17+
1618
namespace SST::Core::Serialization::pvt {
1719

1820
void

src/sst/core/serialization/impl/serialize_array.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"The header file sst/core/serialization/impl/serialize_array.h should not be directly included as it is not part of the stable public API. The file is included in sst/core/serialization/serialize.h"
1818
#endif
1919

20+
#include "sst/core/serialization/impl/serialize_utility.h"
2021
#include "sst/core/serialization/serializer.h"
2122

2223
#include <array>
@@ -68,12 +69,6 @@ serialize_array_map_element(serializer& ser, void* data, ser_opt_t opt, size_t i
6869
sst_ser_object(ser, static_cast<ELEM_T*>(data)[index], opt, name);
6970
}
7071

71-
// Whether the element type is copyable with memcpy()
72-
// TODO: Implement with std::is_trivially_copyable and std::is_aggregate, using reflection to check for troublesome
73-
// members like pointers
74-
template <typename T>
75-
constexpr bool is_trivial_element_v = std::is_arithmetic_v<T> || std::is_enum_v<T>;
76-
7772
// Serialize fixed arrays
7873
// ELEM_T: Array element type
7974
// SIZE: Fixed array size
@@ -99,7 +94,9 @@ struct serialize_impl_fixed_array
9994
[[fallthrough]];
10095

10196
default:
102-
if constexpr ( is_trivial_element_v<ELEM_T> )
97+
// TODO: How to handle array-of-array
98+
// is_trivially_serializable_excluded_v<ELEM_T> can be added to exclude arrays-of-arrays from the fast path
99+
if constexpr ( is_trivially_serializable_v<ELEM_T> )
103100
ser.raw(&(*aPtr)[0], sizeof(ELEM_T) * SIZE);
104101
else
105102
serialize_array(ser, &(*aPtr)[0], elem_opt, SIZE, serialize_array_element<ELEM_T>);
@@ -152,7 +149,9 @@ class serialize_impl<pvt::array_wrapper<ELEM_T, SIZE_T>>
152149
break;
153150

154151
default:
155-
if constexpr ( std::is_void_v<ELEM_T> || pvt::is_trivial_element_v<ELEM_T> )
152+
// TODO: How to handle array-of-array
153+
// is_trivially_serializable_excluded_v<ELEM_T> can be added to exclude arrays-of-arrays from the fast path
154+
if constexpr ( std::is_void_v<ELEM_T> || is_trivially_serializable_v<ELEM_T> )
156155
ser.binary(ary.ptr, ary.size);
157156
else {
158157
ser.primitive(ary.size);
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// Copyright 2009-2025 NTESS. Under the terms
2+
// of Contract DE-NA0003525 with NTESS, the U.S.
3+
// Government retains certain rights in this software.
4+
//
5+
// Copyright (c) 2009-2025, NTESS
6+
// All rights reserved.
7+
//
8+
// This file is part of the SST software package. For license
9+
// information, see the LICENSE file in the top level directory of the
10+
// distribution.
11+
12+
#include "sst_config.h"
13+
14+
#include "sst/core/serialization/serialize.h"
15+
16+
#include <array>
17+
#include <bitset>
18+
#include <complex>
19+
#include <cstddef>
20+
#include <cstdint>
21+
#include <type_traits>
22+
#include <utility>
23+
#include <vector>
24+
25+
namespace SST::Core::Serialization::unittest {
26+
27+
// Compile-time unit tests of trivially serializable types
28+
29+
template <typename TYPE, bool EXPECTED>
30+
void
31+
trivially_serializable_test()
32+
{
33+
static_assert(is_trivially_serializable_v<TYPE> == EXPECTED);
34+
}
35+
36+
template <typename TYPE>
37+
void
38+
test_trivially_serializable_type_assumptions()
39+
{
40+
static_assert(std::is_trivially_copyable_v<TYPE> && std::is_standard_layout_v<TYPE>);
41+
}
42+
43+
template void trivially_serializable_test<std::byte, true>();
44+
template void trivially_serializable_test<int8_t, true>();
45+
template void trivially_serializable_test<uint8_t, true>();
46+
template void trivially_serializable_test<int16_t, true>();
47+
template void trivially_serializable_test<uint16_t, true>();
48+
template void trivially_serializable_test<int32_t, true>();
49+
template void trivially_serializable_test<uint32_t, true>();
50+
template void trivially_serializable_test<int64_t, true>();
51+
template void trivially_serializable_test<uint64_t, true>();
52+
template void trivially_serializable_test<float, true>();
53+
template void trivially_serializable_test<double, true>();
54+
template void trivially_serializable_test<long double, true>();
55+
56+
enum class test_enum { X, Y, Z };
57+
enum class test_enum_int8_t : int8_t { X, Y, Z, A, B, C };
58+
enum class test_enum_uint8_t : uint8_t { X, Y, Z, A, B, C, J, K, L };
59+
enum class test_enum_int16_t : int16_t { X, Y, Z, A, B, C };
60+
enum class test_enum_uint16_t : uint16_t { X, Y, Z, A, B, C, J, K, L };
61+
enum class test_enum_int32_t : int32_t { X, Y, Z, A, B, C };
62+
enum class test_enum_uint32_t : uint32_t { X, Y, Z, A, B, C, J, K, L };
63+
enum class test_enum_int64_t : int64_t { X, Y, Z, A, B, C };
64+
enum class test_enum_uint64_t : uint64_t { X, Y, Z, A, B, C, J, K, L };
65+
66+
template void trivially_serializable_test<test_enum, true>();
67+
template void trivially_serializable_test<test_enum_int8_t, true>();
68+
template void trivially_serializable_test<test_enum_uint8_t, true>();
69+
template void trivially_serializable_test<test_enum_int16_t, true>();
70+
template void trivially_serializable_test<test_enum_uint16_t, true>();
71+
template void trivially_serializable_test<test_enum_int32_t, true>();
72+
template void trivially_serializable_test<test_enum_uint32_t, true>();
73+
template void trivially_serializable_test<test_enum_int64_t, true>();
74+
template void trivially_serializable_test<test_enum_uint64_t, true>();
75+
76+
struct test_complex_float
77+
{
78+
float r, i;
79+
};
80+
struct test_complex_double
81+
{
82+
double r, i;
83+
};
84+
85+
template void trivially_serializable_test<test_complex_float, true>();
86+
template void trivially_serializable_test<test_complex_double, true>();
87+
template void trivially_serializable_test<std::complex<float>, true>();
88+
template void trivially_serializable_test<std::complex<double>, true>();
89+
90+
struct test_complex_mix
91+
{
92+
float _Complex cf;
93+
double _Complex cd;
94+
long double _Complex cl;
95+
std::complex<double> cfa[10];
96+
};
97+
98+
template void trivially_serializable_test<test_complex_mix, true>();
99+
100+
struct test_complex_double_array
101+
{
102+
test_complex_double ary[1000];
103+
};
104+
105+
template void trivially_serializable_test<test_complex_double_array, true>();
106+
107+
struct test_struct
108+
{
109+
test_complex_float z[8];
110+
std::bitset<100> bs;
111+
test_complex_float test_struct::* mem_ptr;
112+
std::array<size_t, 100> sizes;
113+
union {
114+
int i;
115+
float f;
116+
};
117+
};
118+
119+
template void trivially_serializable_test<test_struct, true>();
120+
121+
union test_union_ptr_2nd {
122+
uintptr_t iptr;
123+
void* ptr;
124+
};
125+
126+
template void trivially_serializable_test<test_union_ptr_2nd, true>();
127+
128+
// Tests of types which are not trivially serializable
129+
130+
template void trivially_serializable_test<void, false>();
131+
template void trivially_serializable_test<void*, false>();
132+
133+
typedef void (*fptr)(int, int);
134+
template void trivially_serializable_test<fptr, false>();
135+
136+
typedef test_complex_double* test_complex_double_array_ptr[1000];
137+
template void trivially_serializable_test<test_complex_double_array_ptr, false>();
138+
139+
template void trivially_serializable_test<std::vector<int>, false>();
140+
141+
struct test_struct_ref
142+
{
143+
test_complex_float z[8];
144+
double& dref;
145+
};
146+
147+
template void trivially_serializable_test<test_struct_ref, false>();
148+
149+
struct test_struct_ptr
150+
{
151+
int i;
152+
int* iptr;
153+
double d;
154+
};
155+
156+
template void trivially_serializable_test<test_struct_ptr, false>();
157+
158+
struct test_serialize_order
159+
{
160+
std::complex<double> cfa[10];
161+
void serialize_order(serializer& ser) { SST_SER(cfa); }
162+
};
163+
164+
template void trivially_serializable_test<test_serialize_order, false>();
165+
166+
struct test_mix
167+
{
168+
test_complex_float z;
169+
test_struct_ref refs;
170+
};
171+
172+
template void trivially_serializable_test<test_mix, false>();
173+
174+
union test_union_ptr_1st {
175+
void* ptr;
176+
uintptr_t iptr;
177+
};
178+
179+
template void trivially_serializable_test<test_union_ptr_1st, false>();
180+
181+
struct test_container_struct
182+
{
183+
int size;
184+
std::vector<double> v;
185+
};
186+
187+
template void trivially_serializable_test<test_container_struct, false>();
188+
189+
template void trivially_serializable_test<std::pair<int, int>, false>();
190+
191+
template void trivially_serializable_test<std::tuple<float, float, float>, false>();
192+
193+
// Test assumptions which are made by is_trivially_serializable_v<TYPE>
194+
template void test_trivially_serializable_type_assumptions<std::bitset<1>>();
195+
template void test_trivially_serializable_type_assumptions<std::bitset<8>>();
196+
template void test_trivially_serializable_type_assumptions<std::bitset<100>>();
197+
template void test_trivially_serializable_type_assumptions<std::bitset<200>>();
198+
template void test_trivially_serializable_type_assumptions<std::bitset<400>>();
199+
template void test_trivially_serializable_type_assumptions<std::bitset<800>>();
200+
template void test_trivially_serializable_type_assumptions<std::bitset<1600>>();
201+
202+
template void test_trivially_serializable_type_assumptions<std::complex<float>>();
203+
template void test_trivially_serializable_type_assumptions<std::complex<double>>();
204+
template void test_trivially_serializable_type_assumptions<std::complex<long double>>();
205+
206+
} // namespace SST::Core::Serialization::unittest
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2009-2025 NTESS. Under the terms
2+
// of Contract DE-NA0003525 with NTESS, the U.S.
3+
// Government retains certain rights in this software.
4+
//
5+
// Copyright (c) 2009-2025, NTESS
6+
// All rights reserved.
7+
//
8+
// This file is part of the SST software package. For license
9+
// information, see the LICENSE file in the top level directory of the
10+
// distribution.
11+
12+
#ifndef SST_CORE_SERIALIZATION_IMPL_SERIALIZE_TRIVIAL_H
13+
#define SST_CORE_SERIALIZATION_IMPL_SERIALIZE_TRIVIAL_H
14+
15+
#ifndef SST_INCLUDING_SERIALIZE_H
16+
#warning \
17+
"The header file sst/core/serialization/impl/serialize_trivial.h should not be directly included as it is not part of the stable public API. The file is included in sst/core/serialization/serialize.h"
18+
#endif
19+
20+
#include "sst/core/serialization/impl/serialize_utility.h"
21+
#include "sst/core/serialization/serializer.h"
22+
23+
#include <array>
24+
#include <type_traits>
25+
#include <utility>
26+
27+
namespace SST::Core::Serialization {
28+
29+
// Types excluded because they would cause ambiguity with more specialized methods
30+
template <typename T>
31+
struct is_trivially_serializable_excluded : std::is_array<T>
32+
{};
33+
34+
template <typename T, size_t S>
35+
struct is_trivially_serializable_excluded<std::array<T, S>> : std::true_type
36+
{};
37+
38+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
39+
// Version of serialize that works for trivially serializable types which aren't excluded, and pointers thereof //
40+
// //
41+
// Note that the pointer tracking happens at a higher level, and only if it is turned on. If it is not turned //
42+
// on, then this only copies the value pointed to into the buffer. If multiple objects point to the same //
43+
// location, they will each have an independent copy after deserialization. //
44+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
45+
46+
template <class T>
47+
class serialize_impl<T,
48+
std::enable_if_t<std::conjunction_v<std::negation<is_trivially_serializable_excluded<std::remove_pointer_t<T>>>,
49+
is_trivially_serializable<std::remove_pointer_t<T>>>>>
50+
{
51+
void operator()(T& t, serializer& ser, ser_opt_t options)
52+
{
53+
switch ( const auto mode = ser.mode() ) {
54+
case serializer::MAP:
55+
// Right now only arithmetic and enum types are handled in mapping mode without custom serializer
56+
if constexpr ( std::is_arithmetic_v<std::remove_pointer_t<T>> ||
57+
std::is_enum_v<std::remove_pointer_t<T>> ) {
58+
ObjectMap* obj_map;
59+
if constexpr ( std::is_pointer_v<T> )
60+
obj_map = new ObjectMapFundamental<std::remove_pointer_t<T>>(t);
61+
else
62+
obj_map = new ObjectMapFundamental<T>(&t);
63+
if ( SerOption::is_set(options, SerOption::map_read_only) ) ser.mapper().setNextObjectReadOnly();
64+
ser.mapper().map_primitive(ser.getMapName(), obj_map);
65+
}
66+
else {
67+
// TODO: Handle mapping mode for trivially serializable types without from_string() methods which do not
68+
// define their own serialization methods.
69+
}
70+
break;
71+
72+
default:
73+
if constexpr ( std::is_pointer_v<T> ) {
74+
if ( mode == serializer::UNPACK ) t = new std::remove_pointer_t<T> {};
75+
ser.primitive(*t);
76+
}
77+
else {
78+
ser.primitive(t);
79+
}
80+
break;
81+
}
82+
}
83+
84+
SST_FRIEND_SERIALIZE();
85+
};
86+
87+
} // namespace SST::Core::Serialization
88+
89+
#endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_TRIVIAL_H

src/sst/core/serialization/impl/serialize_tuple.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
#endif
1919

2020
#include "sst/core/serialization/impl/serialize_utility.h"
21-
#include "sst/core/serialization/serialize.h"
2221
#include "sst/core/serialization/serializer.h"
2322

2423
#include <tuple>
24+
#include <type_traits>
2525

2626
namespace SST::Core::Serialization {
2727

0 commit comments

Comments
 (0)