44
55#include < memory>
66#include < optional>
7+ #include < glog/logging.h>
78
89#include " mutex.h"
910
@@ -94,6 +95,8 @@ class OffsetAllocationHandle {
9495 // The real base and requested size of the allocated memory.
9596 uint64_t real_base;
9697 uint64_t requested_size;
98+
99+ friend class OffsetAllocatorTest ; // for unit tests
97100};
98101
99102struct OffsetAllocatorMetrics {
@@ -155,6 +158,13 @@ class OffsetAllocator : public std::enable_shared_from_this<OffsetAllocator> {
155158 [[nodiscard]]
156159 OffsetAllocatorMetrics get_metrics () const ;
157160
161+ // Serialize the allocator with serializer.
162+ template <typename T>
163+ void serialize_to (T& serializer) const ;
164+
165+ template <typename T>
166+ static std::shared_ptr<OffsetAllocator> deserialize_from (T& serializer);
167+
158168 private:
159169 friend class OffsetAllocationHandle ;
160170
@@ -166,11 +176,11 @@ class OffsetAllocator : public std::enable_shared_from_this<OffsetAllocator> {
166176 OffsetAllocatorMetrics get_metrics_internal () const ;
167177
168178 std::unique_ptr<__Allocator> m_allocator GUARDED_BY (m_mutex);
169- const uint64_t m_base;
179+ uint64_t m_base;
170180 // The real offset and size of the allocated memory need to be multiplied by
171181 // m_multiplier
172- const uint64_t m_multiplier_bits;
173- const uint64_t m_capacity;
182+ uint64_t m_multiplier_bits;
183+ uint64_t m_capacity;
174184 mutable Mutex m_mutex;
175185
176186 // Lightweight metrics maintained during allocation/deallocation
@@ -179,12 +189,20 @@ class OffsetAllocator : public std::enable_shared_from_this<OffsetAllocator> {
179189
180190 // Private constructor - use create() factory method instead
181191 OffsetAllocator (uint64_t base, size_t size, uint32 init_capacity,
182- uint32 max_capacity);
192+ uint32 max_capacity);
193+
194+ // Private constructor - initialize from serialized data
195+ template <typename T>
196+ OffsetAllocator (T& serializer);
197+
198+ friend class OffsetAllocatorTest ; // for unit tests
183199};
184200
185201class __Allocator {
186202 public:
187203 __Allocator (uint32 size, uint32 init_capacity, uint32 max_capacity);
204+ template <typename T>
205+ __Allocator (T& serializer) noexcept (false );
188206 __Allocator (__Allocator&& other);
189207 ~__Allocator ();
190208 void reset ();
@@ -196,6 +214,10 @@ class __Allocator {
196214 OffsetAllocStorageReport storageReport () const ;
197215 OffsetAllocStorageReportFull storageReportFull () const ;
198216
217+ // Serialize the allocator with serializer.
218+ template <typename T>
219+ void serialize_to (T& serializer) const ;
220+
199221 private:
200222 uint32 insertNodeIntoBin (uint32 size, uint32 dataOffset);
201223 void removeNodeFromBin (uint32 nodeIndex);
@@ -224,6 +246,103 @@ class __Allocator {
224246 Node* m_nodes;
225247 NodeIndex* m_freeNodes;
226248 uint32 m_freeOffset;
249+
250+ friend class OffsetAllocatorTest ; // for unit tests
227251};
228252
253+ // Template method implementations
254+ template <typename T>
255+ void OffsetAllocator::serialize_to (T& serializer) const {
256+ MutexLocker guard (&m_mutex);
257+
258+ if (!m_allocator) {
259+ serializer.set_error (" Allocator is not initialized" );
260+ return ;
261+ }
262+
263+ // Basic member variables
264+ serializer.write (&m_base, sizeof (m_base));
265+ serializer.write (&m_multiplier_bits, sizeof (m_multiplier_bits));
266+ serializer.write (&m_capacity, sizeof (m_capacity));
267+ serializer.write (&m_allocated_size, sizeof (m_allocated_size));
268+ serializer.write (&m_allocated_num, sizeof (m_allocated_num));
269+ // Serialize the allocator
270+ m_allocator->serialize_to (serializer);
271+ }
272+
273+ template <typename T>
274+ std::shared_ptr<OffsetAllocator> OffsetAllocator::deserialize_from (
275+ T& serializer) {
276+ return std::shared_ptr<OffsetAllocator>(new OffsetAllocator (serializer));
277+ }
278+
279+ template <typename T>
280+ OffsetAllocator::OffsetAllocator (T& serializer) {
281+ // serializer.read() will throw an exception if the buffer is corrupted.
282+ try {
283+ serializer.read (&m_base, sizeof (m_base));
284+ serializer.read (&m_multiplier_bits, sizeof (m_multiplier_bits));
285+ serializer.read (&m_capacity, sizeof (m_capacity));
286+ serializer.read (&m_allocated_size, sizeof (m_allocated_size));
287+ serializer.read (&m_allocated_num, sizeof (m_allocated_num));
288+ m_allocator = std::make_unique<__Allocator>(serializer);
289+ } catch (const std::exception& e) {
290+ LOG (ERROR) << " Deserializing OffsetAllocator failed, error="
291+ << e.what ();
292+ throw std::runtime_error (" Deserializing OffsetAllocator failed" );
293+ }
294+ }
295+
296+ template <typename T>
297+ void __Allocator::serialize_to (T& serializer) const {
298+ if (!m_nodes || !m_freeNodes) {
299+ serializer.set_error (" Allocator is not initialized" );
300+ return ;
301+ }
302+
303+ serializer.write (&m_size, sizeof (m_size));
304+ serializer.write (&m_current_capacity, sizeof (m_current_capacity));
305+ serializer.write (&m_max_capacity, sizeof (m_max_capacity));
306+ serializer.write (&m_freeStorage, sizeof (m_freeStorage));
307+ serializer.write (&m_usedBinsTop, sizeof (m_usedBinsTop));
308+ serializer.write (&m_usedBins, sizeof (m_usedBins));
309+ serializer.write (&m_binIndices, sizeof (m_binIndices));
310+ serializer.write (&m_freeOffset, sizeof (m_freeOffset));
311+ serializer.write (m_nodes, m_current_capacity * sizeof (Node));
312+ serializer.write (m_freeNodes, m_current_capacity * sizeof (NodeIndex));
313+ }
314+
315+ template <typename T>
316+ __Allocator::__Allocator (T& serializer) {
317+ m_nodes = nullptr ;
318+ m_freeNodes = nullptr ;
319+
320+ // serializer.read() will throw an exception if the buffer is corrupted.
321+ try {
322+ // Deserialize basic member variables
323+ serializer.read (&m_size, sizeof (m_size));
324+ serializer.read (&m_current_capacity, sizeof (m_current_capacity));
325+ serializer.read (&m_max_capacity, sizeof (m_max_capacity));
326+ serializer.read (&m_freeStorage, sizeof (m_freeStorage));
327+ serializer.read (&m_usedBinsTop, sizeof (m_usedBinsTop));
328+ serializer.read (&m_usedBins, sizeof (m_usedBins));
329+ serializer.read (&m_binIndices, sizeof (m_binIndices));
330+ serializer.read (&m_freeOffset, sizeof (m_freeOffset));
331+
332+ // Allocate memory for nodes and freeNodes
333+ m_nodes = new Node[m_max_capacity];
334+ m_freeNodes = new NodeIndex[m_max_capacity];
335+
336+ // Deserialize the arrays
337+ serializer.read (m_nodes, m_current_capacity * sizeof (Node));
338+ serializer.read (m_freeNodes, m_current_capacity * sizeof (NodeIndex));
339+ } catch (const std::exception& e) {
340+ // Free memory if deserialization fails
341+ LOG (ERROR) << " Deserializing __Allocator failed, error=" << e.what ();
342+ if (m_nodes) delete[] m_nodes;
343+ if (m_freeNodes) delete[] m_freeNodes;
344+ throw std::runtime_error (" Deserializing __Allocator failed" );
345+ }
346+ }
347+
229348} // namespace mooncake::offset_allocator
0 commit comments