Skip to content

Commit b49fff5

Browse files
committed
Handle trivially serializable types (types like arithmetic and enum types which can be read and written as raw bytes)
1 parent f642e80 commit b49fff5

File tree

5 files changed

+407
-70
lines changed

5 files changed

+407
-70
lines changed

src/sst/core/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ nobase_dist_sst_HEADERS = \
109109
serialization/impl/packer.h \
110110
serialization/impl/sizer.h \
111111
serialization/impl/serialize_string.h \
112+
serialization/impl/serialize_trivial.h \
112113
serialization/impl/unpacker.h \
113114
serialization/serializer.h \
114115
serialization/serializer_fwd.h \

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: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
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 <complex>
25+
#include <cstddef>
26+
#include <cstdint>
27+
#include <type_traits>
28+
#include <utility>
29+
#include <vector>
30+
31+
namespace SST::Core::Serialization {
32+
33+
// Types excluded because they would cause ambiguity with more specialized methods
34+
template <typename T>
35+
constexpr bool is_trivially_serializable_excluded_v = std::is_array_v<T>;
36+
37+
template <typename T, size_t S>
38+
constexpr bool is_trivially_serializable_excluded_v<std::array<T, S>> = true;
39+
40+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
41+
// Version of serialize that works for trivially serializable types which aren't excluded, and pointers thereof
42+
//
43+
// Note that the pointer tracking happens at a higher level, and only if it is turned on. If it is not turned on, then
44+
// this only copies the value pointed to into the buffer. If multiple objects point to the same location, they will each
45+
// have an independent copy after deserialization.
46+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
47+
48+
template <class T>
49+
class serialize_impl<T, std::enable_if_t<is_trivially_serializable_v<std::remove_pointer_t<T>> &&
50+
!is_trivially_serializable_excluded_v<std::remove_pointer_t<T>>>>
51+
{
52+
void operator()(T& t, serializer& ser, ser_opt_t options)
53+
{
54+
switch ( const auto mode = ser.mode() ) {
55+
case serializer::MAP:
56+
// Right now only arithmetic and enum types are handled in mapping mode without custom serializer
57+
if constexpr ( std::is_arithmetic_v<std::remove_pointer_t<T>> ||
58+
std::is_enum_v<std::remove_pointer_t<T>> ) {
59+
ObjectMap* obj_map;
60+
if constexpr ( std::is_pointer_v<T> )
61+
obj_map = new ObjectMapFundamental<std::remove_pointer_t<T>>(t);
62+
else
63+
obj_map = new ObjectMapFundamental<T>(&t);
64+
if ( SerOption::is_set(options, SerOption::map_read_only) ) ser.mapper().setNextObjectReadOnly();
65+
ser.mapper().map_primitive(ser.getMapName(), obj_map);
66+
}
67+
else {
68+
// TODO: Handle mapping mode for trivially serializable types without from_string() methods which do not
69+
// define their own serialization methods.
70+
}
71+
break;
72+
73+
default:
74+
if constexpr ( std::is_pointer_v<T> ) {
75+
if ( mode == serializer::UNPACK ) t = new std::remove_pointer_t<T> {};
76+
ser.primitive(*t);
77+
}
78+
else {
79+
ser.primitive(t);
80+
}
81+
break;
82+
}
83+
}
84+
85+
SST_FRIEND_SERIALIZE();
86+
};
87+
88+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
89+
// Compile-time unit tests
90+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
91+
92+
namespace unittest {
93+
94+
template <typename TYPE, bool EXPECTED>
95+
void
96+
trivially_serializable_test()
97+
{
98+
static_assert(is_trivially_serializable_v<TYPE> == EXPECTED);
99+
}
100+
101+
template void trivially_serializable_test<std::byte, true>();
102+
template void trivially_serializable_test<int8_t, true>();
103+
template void trivially_serializable_test<uint8_t, true>();
104+
template void trivially_serializable_test<int16_t, true>();
105+
template void trivially_serializable_test<uint16_t, true>();
106+
template void trivially_serializable_test<int32_t, true>();
107+
template void trivially_serializable_test<uint32_t, true>();
108+
template void trivially_serializable_test<int64_t, true>();
109+
template void trivially_serializable_test<uint64_t, true>();
110+
template void trivially_serializable_test<float, true>();
111+
template void trivially_serializable_test<double, true>();
112+
template void trivially_serializable_test<long double, true>();
113+
114+
enum class test_enum { X, Y, Z };
115+
enum class test_enum_int8_t : int8_t { X, Y, Z, A, B, C };
116+
enum class test_enum_uint8_t : uint8_t { X, Y, Z, A, B, C, J, K, L };
117+
enum class test_enum_int16_t : int16_t { X, Y, Z, A, B, C };
118+
enum class test_enum_uint16_t : uint16_t { X, Y, Z, A, B, C, J, K, L };
119+
enum class test_enum_int32_t : int32_t { X, Y, Z, A, B, C };
120+
enum class test_enum_uint32_t : uint32_t { X, Y, Z, A, B, C, J, K, L };
121+
enum class test_enum_int64_t : int64_t { X, Y, Z, A, B, C };
122+
enum class test_enum_uint64_t : uint64_t { X, Y, Z, A, B, C, J, K, L };
123+
124+
template void trivially_serializable_test<test_enum, true>();
125+
template void trivially_serializable_test<test_enum_int8_t, true>();
126+
template void trivially_serializable_test<test_enum_uint8_t, true>();
127+
template void trivially_serializable_test<test_enum_int16_t, true>();
128+
template void trivially_serializable_test<test_enum_uint16_t, true>();
129+
template void trivially_serializable_test<test_enum_int32_t, true>();
130+
template void trivially_serializable_test<test_enum_uint32_t, true>();
131+
template void trivially_serializable_test<test_enum_int64_t, true>();
132+
template void trivially_serializable_test<test_enum_uint64_t, true>();
133+
134+
struct test_complex_float
135+
{
136+
float r, i;
137+
};
138+
struct test_complex_double
139+
{
140+
double r, i;
141+
};
142+
143+
template void trivially_serializable_test<test_complex_float, true>();
144+
template void trivially_serializable_test<test_complex_double, true>();
145+
template void trivially_serializable_test<std::complex<float>, true>();
146+
template void trivially_serializable_test<std::complex<double>, true>();
147+
148+
struct test_complex_double_array
149+
{
150+
test_complex_double ary[1000];
151+
};
152+
153+
template void trivially_serializable_test<test_complex_double_array, true>();
154+
155+
struct test_struct
156+
{
157+
test_complex_float z[8];
158+
test_complex_float test_struct::* mem_ptr;
159+
test_complex_float (test_struct::*mem_fun_ptr)(test_complex_float);
160+
std::array<size_t, 100> sizes;
161+
union {
162+
int i;
163+
float f;
164+
};
165+
};
166+
167+
template void trivially_serializable_test<test_struct, true>();
168+
169+
union test_union_ptr_2nd {
170+
uintptr_t iptr;
171+
void* ptr;
172+
};
173+
174+
template void trivially_serializable_test<test_union_ptr_2nd, true>();
175+
176+
// Tests of types which are not trivially serializable
177+
178+
template void trivially_serializable_test<void, false>();
179+
template void trivially_serializable_test<void*, false>();
180+
181+
typedef void (*fptr)(int, int);
182+
template void trivially_serializable_test<fptr, false>();
183+
184+
typedef test_complex_double* test_complex_double_array_ptr[1000];
185+
template void trivially_serializable_test<test_complex_double_array_ptr, false>();
186+
187+
struct test_struct_ref
188+
{
189+
test_complex_float z[8];
190+
test_complex_float test_struct::* mem_ptr;
191+
double& dref;
192+
test_complex_float (test_struct::*mem_fun_ptr)(test_complex_float);
193+
};
194+
195+
template void trivially_serializable_test<test_struct_ref, false>();
196+
197+
struct test_struct_ptr
198+
{
199+
test_complex_float test_struct::* mem_ptr;
200+
int* iptr;
201+
};
202+
203+
template void trivially_serializable_test<test_struct_ptr, false>();
204+
205+
struct test_mix
206+
{
207+
test_complex_float z;
208+
test_struct_ref refs;
209+
};
210+
211+
template void trivially_serializable_test<test_mix, false>();
212+
213+
union test_union_ptr_1st {
214+
void* ptr;
215+
uintptr_t iptr;
216+
};
217+
218+
template void trivially_serializable_test<test_union_ptr_1st, false>();
219+
220+
struct test_container_struct
221+
{
222+
int size;
223+
std::vector<double> v;
224+
};
225+
226+
template void trivially_serializable_test<test_container_struct, false>();
227+
228+
template void trivially_serializable_test<std::pair<int, int>, false>();
229+
230+
template void trivially_serializable_test<std::tuple<float, float, float>, false>();
231+
232+
} // namespace unittest
233+
234+
} // namespace SST::Core::Serialization
235+
236+
#endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_TRIVIAL_H

0 commit comments

Comments
 (0)