SST 16.0.0
Structural Simulation Toolkit
serialize_array.h
1// Copyright 2009-2026 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-2026, 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_ARRAY_H
13#define SST_CORE_SERIALIZATION_IMPL_SERIALIZE_ARRAY_H
14
15#ifndef SST_INCLUDING_SERIALIZE_H
16#warning \
17 "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"
18#endif
19
20#include "sst/core/serialization/serializer.h"
21
22#include <array>
23#include <cstddef>
24#include <type_traits>
25
26namespace SST::Core::Serialization {
27
28// Serialize arrays
29
30namespace pvt {
31
32// Wrapper classes. They have no declared constructors so that they can be aggregate-initialized.
33template <typename ELEM_T, typename SIZE_T>
35{
36 ELEM_T*& ptr;
37 SIZE_T& size;
38};
39
40template <typename ELEM_T>
42{
43 ELEM_T*& ptr;
44};
45
46} // namespace pvt
47
48// Wrapper functions
49
50template <typename ELEM_T, typename SIZE_T>
52array(ELEM_T*& ptr, SIZE_T& size)
53{
54 return { ptr, size };
55}
56
57template <typename SIZE_T>
58pvt::array_wrapper<void, SIZE_T>
59buffer(void*& ptr, SIZE_T& size)
60{
61 return { ptr, size };
62}
63
64template <typename ELEM_T>
66raw_ptr(ELEM_T*& ptr)
67{
68 return { ptr };
69}
70
71/**
72 Class used to map unbounded arrays with runtime size
73 */
74template <class ELEM_T, class SIZE_T>
75class ObjectMapArray : public ObjectMapContainer<ELEM_T>
76{
77protected:
78 SIZE_T& size_;
79
80public:
81 virtual size_t getSize() const { return size_; }
82 ObjectMapArray(ELEM_T* addr, SIZE_T& size) :
83 ObjectMapContainer<ELEM_T>(addr),
84 size_(size)
85 {}
86 void refresh() override
87 {
88 // Decrement the reference count of all children, which will be replaced
89 for ( const auto& [name, child] : this->variables_ )
90 if ( child != nullptr ) child->decRefCount();
91
92 // Replace the children with new children by re-serializing the array
93 // ObjectMapSerialization(array(this->addr_, size_)) returns an ObjectMap* to a new serialization of the array
94 // dynamic_cast<ObjectMapArray&> downcasts the returned ObjectMap to this ObjectMapArray
95 // std::move improves performance, by shallow-moving the new variables_ into this class's variables_
96 this->variables_ =
97 std::move(dynamic_cast<ObjectMapArray&>(*ObjectMapSerialization(array(this->addr_, size_))).variables_);
98 }
99 ~ObjectMapArray() override = default;
100};
101
102namespace pvt {
103
104// Functions for serializing arrays element by element
105void serialize_array(serializer& ser, void* data, ser_opt_t opt, size_t size,
106 void serialize_array_element(serializer& ser, void* data, ser_opt_t opt, size_t index));
107
108void serialize_array_map(serializer& ser, void* data, ser_opt_t opt, size_t size, ObjectMap* map,
109 void serialize_array_map_element(serializer& ser, void* data, ser_opt_t opt, size_t index, const char* name));
110
111// Serialize an array element
112// Separated out to reduce code size
113template <typename ELEM_T>
114void
115serialize_array_element(serializer& ser, void* data, ser_opt_t opt, size_t index)
116{
117 SST_SER(static_cast<ELEM_T*>(data)[index], opt);
118}
119
120// Serialize an array map element
121// Separated out to reduce code size
122template <typename ELEM_T>
123void
124serialize_array_map_element(serializer& ser, void* data, ser_opt_t opt, size_t index, const char* name)
125{
126 SST_SER_NAME(static_cast<ELEM_T*>(data)[index], name, opt);
127}
128
129// Serialize fixed arrays
130// ELEM_T: Array element type
131// SIZE: Fixed array size
132// OBJ_TYPE: Complete type of object being serialized
133template <typename OBJ_TYPE, typename ELEM_T, size_t SIZE>
135{
136 void operator()(OBJ_TYPE& ary, serializer& ser, ser_opt_t opt)
137 {
138 using T = std::remove_pointer_t<OBJ_TYPE>;
139 ser_opt_t elem_opt = SerOption::is_set(opt, SerOption::as_ptr_elem) ? SerOption::as_ptr : SerOption::none;
140 const auto& aPtr = get_ptr(ary); // reference to ary if it's a pointer; &ary otherwise
141 switch ( ser.mode() ) {
142 case serializer::MAP:
143 serialize_array_map(
144 ser, &(*aPtr)[0], elem_opt, SIZE, new ObjectMapContainer<T>(aPtr), serialize_array_map_element<ELEM_T>);
145 break;
146
147 case serializer::UNPACK:
148 if constexpr ( std::is_pointer_v<OBJ_TYPE> ) {
149 // for pointers to fixed arrays, we allocate the storage
150 if constexpr ( std::is_array_v<T> )
151 ary = reinterpret_cast<OBJ_TYPE>(new std::remove_extent_t<T>[std::extent_v<T>]);
152 else
153 ary = reinterpret_cast<OBJ_TYPE>(new T);
154 }
155 [[fallthrough]];
156
157 default:
158 // TODO: How to handle array-of-array
159 // is_trivially_serializable_excluded_v<ELEM_T> can be added to exclude arrays-of-arrays from the fast path
160 if constexpr ( is_trivially_serializable_v<ELEM_T> )
161 ser.raw(&(*aPtr)[0], sizeof(ELEM_T) * SIZE);
162 else
163 serialize_array(ser, &(*aPtr)[0], elem_opt, SIZE, serialize_array_element<ELEM_T>);
164 break;
165 }
166 }
167};
168
169} // namespace pvt
170
171// Serialize fixed arrays and pointers to them
172template <typename ELEM_T, size_t SIZE>
173class serialize_impl<ELEM_T[SIZE]> : pvt::serialize_impl_fixed_array<ELEM_T[SIZE], ELEM_T, SIZE>
174{
175 SST_FRIEND_SERIALIZE();
176};
177
178template <typename ELEM_T, size_t SIZE>
179class serialize_impl<std::array<ELEM_T, SIZE>> : pvt::serialize_impl_fixed_array<std::array<ELEM_T, SIZE>, ELEM_T, SIZE>
180{
181 SST_FRIEND_SERIALIZE();
182};
183
184template <typename ELEM_T, size_t SIZE>
185class serialize_impl<ELEM_T (*)[SIZE]> : pvt::serialize_impl_fixed_array<ELEM_T (*)[SIZE], ELEM_T, SIZE>
186{
187 SST_FRIEND_SERIALIZE();
188};
189
190template <typename ELEM_T, size_t SIZE>
191class serialize_impl<std::array<ELEM_T, SIZE>*> :
192 pvt::serialize_impl_fixed_array<std::array<ELEM_T, SIZE>*, ELEM_T, SIZE>
193{
194 SST_FRIEND_SERIALIZE();
195};
196
197// Serialize dynamic arrays
198template <typename ELEM_T, typename SIZE_T>
199class serialize_impl<pvt::array_wrapper<ELEM_T, SIZE_T>>
200{
201 void operator()(pvt::array_wrapper<ELEM_T, SIZE_T>& ary, serializer& ser, ser_opt_t opt)
202 {
203 ser_opt_t elem_opt = SerOption::is_set(opt, SerOption::as_ptr_elem) ? SerOption::as_ptr : SerOption::none;
204 const auto mode = ser.mode();
205 size_t size = 0;
206
207 if ( mode != serializer::UNPACK )
208 size = get_array_size(ary.size, "Serialization Error: Array size in SST::Core::Serialization::array() "
209 "cannot fit inside size_t. size_t should be used for array sizes.\n");
210
211 if ( mode == serializer::MAP ) {
212 if constexpr ( !std::is_void_v<ELEM_T> )
213 pvt::serialize_array_map(ser, ary.ptr, elem_opt, size,
214 new ObjectMapArray<ELEM_T, SIZE_T>(ary.ptr, ary.size), pvt::serialize_array_map_element<ELEM_T>);
215 else
216 ser.binary(ary.ptr, ary.size);
217 return;
218 }
219
220 // TODO: How to handle array-of-array
221 // is_trivially_serializable_excluded_v<ELEM_T> can be added to exclude arrays-of-arrays from the fast path
222 if constexpr ( std::is_void_v<ELEM_T> || is_trivially_serializable_v<ELEM_T> )
223 ser.binary(ary.ptr, ary.size);
224 else {
225 ser.primitive(ary.size);
226 if ( mode == serializer::UNPACK ) {
227 size = get_array_size(ary.size, "Serialization Error: Array size in SST::Core::Serialization::array() "
228 "cannot fit inside size_t. size_t should be used for array sizes.\n");
229 ary.ptr = new ELEM_T[size];
230 }
231 pvt::serialize_array(ser, ary.ptr, elem_opt, size, pvt::serialize_array_element<ELEM_T>);
232 }
233 }
234
235 SST_FRIEND_SERIALIZE();
236};
237
238/**
239 Version of serialize that works for copying raw pointers (only
240 copying the value of the pointer. Note that this is only useful
241 if the value is going to be sent back to the originator, since it
242 won't be valid on the other rank.
243 */
244template <typename ELEM_T>
245struct serialize_impl<pvt::raw_ptr_wrapper<ELEM_T>>
246{
247 void operator()(pvt::raw_ptr_wrapper<ELEM_T>& a, serializer& ser, ser_opt_t UNUSED(opt)) { ser.primitive(a.ptr); }
248 SST_FRIEND_SERIALIZE();
249};
250
251} // namespace SST::Core::Serialization
252
253#endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_ARRAY_H
Class used to map unbounded arrays with runtime size.
Definition serialize_array.h:76
void refresh() override
Refresh the ObjectMap, reconstructing children.
Definition serialize_array.h:86
Class used to map containers.
Definition objectMap.h:1420
ObjectMultimap variables_
Map that child ObjectMaps are stored in.
Definition objectMap.h:621
Base class for objects created by the serializer mapping mode used to map the variables for objects.
Definition objectMap.h:188
Base serialize class.
Definition serialize.h:132
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition serializer.h:43
Definition serialize_array.h:35
Definition serialize_array.h:42