4
4
#include " ../../Strings/String.h"
5
5
#include " ../../Testing/Testing.h"
6
6
#include " ../Vector.h"
7
+ #include " ../VectorMap.h"
8
+ #include " ../VectorSet.h"
7
9
8
10
namespace SC
9
11
{
@@ -19,9 +21,14 @@ struct SC::GlobalsContainerTest : public SC::TestCase
19
21
{
20
22
virtualGlobal ();
21
23
}
24
+ if (test_section (" virtual memory dump" ))
25
+ {
26
+ virtualMemoryDump ();
27
+ }
22
28
}
23
29
24
30
void virtualGlobal ();
31
+ void virtualMemoryDump ();
25
32
};
26
33
27
34
void SC::GlobalsContainerTest::virtualGlobal ()
@@ -40,6 +47,85 @@ void SC::GlobalsContainerTest::virtualGlobal()
40
47
Globals::pop (Globals::ThreadLocal);
41
48
}
42
49
50
+ void SC::GlobalsContainerTest::virtualMemoryDump ()
51
+ {
52
+ // ! [GlobalContainerVirtualMemoryDumpSnippet]
53
+ // -----------------------------------------------------------------------------
54
+ // Example showing how to dump and restore a complex struct to a flat buffer.
55
+ // SC::Segment based containers use relative pointers to make this possible.
56
+ // DO NOT use this approach when versioning is needed, that means needing to
57
+ // de-serialize after adding, removing or moving fields in the structure.
58
+ // In such cases consider using SC::SerializationBinary (versioned reflection).
59
+ // -----------------------------------------------------------------------------
60
+ struct NestedStruct
61
+ {
62
+ VectorMap<String, int > someMap;
63
+ VectorSet<int > someSet;
64
+ };
65
+ struct ComplexStruct
66
+ {
67
+ Vector<String> someStrings;
68
+ int someField = 0 ;
69
+ String singleString;
70
+ NestedStruct nestedStruct;
71
+ };
72
+ Buffer memoryDump;
73
+
74
+ // Setup a Virtual Memory allocator with the max upper memory bound
75
+ VirtualMemory virtualMemory;
76
+ SC_TEST_EXPECT (virtualMemory.reserve (1024 * 1024 )); // 1MB is enough here
77
+ VirtualAllocator allocator = {virtualMemory};
78
+ Globals globals = {allocator};
79
+
80
+ // Make the allocator current before creating a ComplexStruct
81
+ Globals::push (Globals::Global, globals);
82
+ ComplexStruct& object = *allocator.create <ComplexStruct>();
83
+ object.someField = 42 ;
84
+ object.singleString = " ASDF" ;
85
+ object.someStrings = {" First" , " Second" };
86
+ SC_TEST_EXPECT (object.nestedStruct .someSet .insert (213 ));
87
+ SC_TEST_EXPECT (object.nestedStruct .someMap .insertIfNotExists ({" 1" , 1 }));
88
+
89
+ // Save used bytes to memoryDump, checking that one page has been committed
90
+ Span<const void > memory = {allocator.data (), allocator.size ()};
91
+ SC_TEST_EXPECT (virtualMemory.committedBytes == virtualMemory.getPageSize ());
92
+ SC_TEST_EXPECT (memory.sizeInBytes () < virtualMemory.getPageSize ());
93
+ SC_TEST_EXPECT (memory.data () == &object);
94
+ SC_TEST_EXPECT ((size_t (memoryDump.data ()) % alignof (ComplexStruct)) == 0 );
95
+ Globals::pop (Globals::Global);
96
+
97
+ // Dump AFTER Globals::pop, using default allocator, and release virtual memory
98
+ SC_TEST_EXPECT (memoryDump.append (memory));
99
+ SC_TEST_EXPECT (virtualMemory.release ());
100
+
101
+ // -----------------------------------------------------------------------------
102
+ // Obtain a read-only view over ComplexStruct by re-interpreting the memory dump
103
+ // NOTE: There's no need to call ComplexStruct destructor at end of scope
104
+ // WARN: Using a different struct type or layout than the dumped one is UB
105
+ // -----------------------------------------------------------------------------
106
+ const Span<const void > span = memoryDump.toSpanConst ();
107
+ const ComplexStruct& readonly = *span.start_lifetime_as <const ComplexStruct>();
108
+ SC_TEST_EXPECT (readonly.someField == 42 );
109
+ SC_TEST_EXPECT (readonly.singleString == " ASDF" );
110
+ SC_TEST_EXPECT (readonly.someStrings [0 ] == " First" );
111
+ SC_TEST_EXPECT (readonly.someStrings [1 ] == " Second" );
112
+ SC_TEST_EXPECT (readonly.someStrings .size () == 2 );
113
+ SC_TEST_EXPECT (readonly.nestedStruct .someSet .size () == 1 );
114
+ SC_TEST_EXPECT (readonly.nestedStruct .someSet .contains (213 ));
115
+ SC_TEST_EXPECT (*readonly.nestedStruct .someMap .get (" 1" ) == 1 );
116
+
117
+ // -----------------------------------------------------------------------------
118
+ // To modify the struct again, copy the read-only view to a new object.
119
+ // A Fixed or Virtual allocator can be used here to group sparse allocations in
120
+ // a nice single contiguous buffer, before dumping it again to disk or network.
121
+ // -----------------------------------------------------------------------------
122
+ ComplexStruct modifiable = readonly;
123
+ SC_TEST_EXPECT (modifiable.someStrings [0 ] == " First" );
124
+ modifiable.someStrings [0 ] = " First modified" ;
125
+ SC_TEST_EXPECT (modifiable.someStrings [0 ] == " First modified" );
126
+ // ! [GlobalContainerVirtualMemoryDumpSnippet]
127
+ }
128
+
43
129
namespace SC
44
130
{
45
131
void runGlobalsContainerTest (SC::TestReport& report) { GlobalsContainerTest test (report); }
0 commit comments