Branch data Line data Source code
1 : : #include "test_libmem_all.h"
2 : :
3 : : /**
4 : : * @brief Verify RELEASE_UNUSED physically shrinks a typed point descriptor while preserving the surviving prefix
5 : : *
6 : : * Grows the descriptor past one allocation slab, fills it with a
7 : : * deterministic point pattern, then shrinks it back to four elements
8 : : * with RELEASE_UNUSED. The allocation reserve must drop to one slab,
9 : : * and the surviving prefix must remain readable after the physical
10 : : * shrink. The descriptor is then grown again to prove that the same
11 : : * prefix still survives the shrink/regrow cycle
12 : : *
13 : : * @return Return describing success or failure
14 : : */
15 : 1 : static Return test_libmem_0012_1(void)
16 : : {
17 : 1 : INITTEST;
18 : :
19 : 1 : m_create(point,points);
20 : :
21 : : /* Choose a source length that forces the descriptor to reserve more than one allocation slab */
22 : 1 : const size_t points_per_slab = MEMORY_BLOCK_BYTES / sizeof(point);
23 : 1 : const size_t large_length = points_per_slab + 1;
24 : 1 : const size_t surviving_length = 4;
25 : :
26 : 1 : ASSERT(points_per_slab > 0);
27 : 1 : ASSERT(large_length > surviving_length);
28 : :
29 : : /* Grow past one slab and seed every element with the deterministic point pattern */
30 : 1 : ASSERT(SUCCESS == m_resize(points,large_length));
31 : 1 : ASSERT(SUCCESS == fill_points(points));
32 : :
33 : : /* Record the large reserve so the later RELEASE_UNUSED shrink can prove that capacity was returned */
34 : 1 : const size_t allocated_before_shrink = points->actually_allocated_bytes;
35 : 1 : ASSERT(allocated_before_shrink > MEMORY_BLOCK_BYTES);
36 : :
37 : : /* Shrink to a small prefix and require the reserve to drop back to exactly one slab */
38 : 1 : ASSERT(SUCCESS == m_resize(points,surviving_length,RELEASE_UNUSED));
39 : 1 : ASSERT(points->length == surviving_length);
40 : 1 : ASSERT(points->actually_allocated_bytes == MEMORY_BLOCK_BYTES);
41 : 1 : ASSERT(points->actually_allocated_bytes < allocated_before_shrink);
42 : :
43 : : /* Read the remaining prefix after the physical shrink to catch corrupted surviving elements */
44 : 1 : const point *shrunk_points = m_data_ro(point,points);
45 : 1 : ASSERT(shrunk_points != NULL);
46 : :
47 : 1 : IF(shrunk_points != NULL)
48 : : {
49 [ + + ]: 5 : for(size_t point_index = 0; point_index < surviving_length; point_index++)
50 : : {
51 : 4 : ASSERT(shrunk_points[point_index].x == (int)(point_index * 2 + 1));
52 : 4 : ASSERT(shrunk_points[point_index].y == (int)(point_index * 2 + 2));
53 : : }
54 : : }
55 : :
56 : : /* Grow again inside the retained slab; the already-surviving prefix must still be intact */
57 : 1 : ASSERT(SUCCESS == m_resize(points,8));
58 : 1 : ASSERT(points->length == 8);
59 : 1 : ASSERT(points->actually_allocated_bytes == MEMORY_BLOCK_BYTES);
60 : :
61 : : /* Re-read through a fresh view because any resize may invalidate cached data pointers */
62 : 1 : const point *regrown_points = m_data_ro(point,points);
63 : 1 : ASSERT(regrown_points != NULL);
64 : :
65 : 1 : IF(regrown_points != NULL)
66 : : {
67 [ + + ]: 5 : for(size_t point_index = 0; point_index < surviving_length; point_index++)
68 : : {
69 : 4 : ASSERT(regrown_points[point_index].x == (int)(point_index * 2 + 1));
70 : 4 : ASSERT(regrown_points[point_index].y == (int)(point_index * 2 + 2));
71 : : }
72 : : }
73 : :
74 : : /* Confirm the resize cycle did not accidentally switch the descriptor into string mode */
75 : 1 : ASSERT(points->string_length == 0);
76 : 1 : ASSERT(points->is_string == false);
77 : 1 : call(m_del(points));
78 : :
79 : 1 : RETURN_STATUS;
80 : : }
81 : :
82 : : /**
83 : : * @brief Verify m_concat_data appends a typed source after a RELEASE_UNUSED shrink/regrow cycle
84 : : *
85 : : * Builds a five-element destination prefix and a source descriptor
86 : : * that first crosses a slab boundary, then shrinks with RELEASE_UNUSED,
87 : : * then grows back to eight elements. The final byte-for-byte check
88 : : * proves that m_concat_data keeps the destination prefix intact and
89 : : * appends the full regrown source in order
90 : : *
91 : : * @return Return describing success or failure
92 : : */
93 : 1 : static Return test_libmem_0012_2(void)
94 : : {
95 : 1 : INITTEST;
96 : :
97 : 1 : m_create(point,points);
98 : 1 : m_create(point,mirror);
99 : :
100 : : /* Use the same slab-crossing source size as the shrink/regrow test */
101 : 1 : const size_t points_per_slab = MEMORY_BLOCK_BYTES / sizeof(point);
102 : 1 : const size_t large_length = points_per_slab + 1;
103 : :
104 : 1 : ASSERT(points_per_slab > 0);
105 : :
106 : : /* Prepare the destination prefix that must survive the append untouched */
107 : 1 : ASSERT(SUCCESS == m_resize(mirror,5));
108 : 1 : ASSERT(SUCCESS == fill_points(mirror));
109 : :
110 : : /* Prepare a source descriptor large enough for RELEASE_UNUSED to reclaim real capacity */
111 : 1 : ASSERT(SUCCESS == m_resize(points,large_length));
112 : 1 : ASSERT(SUCCESS == fill_points(points));
113 : :
114 : : /* Capture the oversized reserve before shrinking the source */
115 : 1 : const size_t allocated_before_shrink = points->actually_allocated_bytes;
116 : 1 : ASSERT(allocated_before_shrink > MEMORY_BLOCK_BYTES);
117 : :
118 : : /* Physically shrink the source to its first four points before regrowing it for the append */
119 : 1 : ASSERT(SUCCESS == m_resize(points,4,RELEASE_UNUSED));
120 : 1 : ASSERT(points->length == 4);
121 : 1 : ASSERT(points->actually_allocated_bytes == MEMORY_BLOCK_BYTES);
122 : 1 : ASSERT(points->actually_allocated_bytes < allocated_before_shrink);
123 : :
124 : : /* Regrow the source and write the newly exposed tail so the expected concat payload is complete */
125 : 1 : ASSERT(SUCCESS == m_resize(points,8));
126 : :
127 : 1 : point *regrown_points = m_data(point,points);
128 : 1 : ASSERT(regrown_points != NULL);
129 : :
130 : 1 : IF(regrown_points != NULL)
131 : : {
132 : 1 : regrown_points[4] = (point){9,10};
133 : 1 : regrown_points[5] = (point){11,12};
134 : 1 : regrown_points[6] = (point){13,14};
135 : 1 : regrown_points[7] = (point){15,16};
136 : : }
137 : :
138 : : /* Append all eight source points after the five-point destination prefix */
139 : 1 : ASSERT(SUCCESS == m_concat_data(mirror,points));
140 : :
141 : : /* Check the entire destination, not only the last appended point */
142 : 1 : const point *mirror_view = m_data_ro(point,mirror);
143 : 1 : ASSERT(mirror_view != NULL);
144 : :
145 : 1 : IF(mirror_view != NULL)
146 : : {
147 : 1 : const point expected[] = {
148 : : {1,2},{3,4},{5,6},{7,8},{9,10},
149 : : {1,2},{3,4},{5,6},{7,8},{9,10},{11,12},{13,14},{15,16}
150 : : };
151 : 1 : const size_t expected_length = sizeof(expected) / sizeof(point);
152 : :
153 : 1 : ASSERT(mirror->length == expected_length);
154 : 1 : ASSERT(memcmp(mirror_view,expected,sizeof(expected)) == 0);
155 : : }
156 : :
157 : : /* Both descriptors must remain ordinary data descriptors after concat */
158 : 1 : ASSERT(points->string_length == 0);
159 : 1 : ASSERT(points->is_string == false);
160 : 1 : ASSERT(mirror->string_length == 0);
161 : 1 : ASSERT(mirror->is_string == false);
162 : 1 : call(m_del(points));
163 : 1 : call(m_del(mirror));
164 : :
165 : 1 : RETURN_STATUS;
166 : : }
167 : :
168 : : /**
169 : : * @brief Verify typed point shrink/regrow behavior and concatenation after RELEASE_UNUSED
170 : : *
171 : : * In plain terms, this suite checks that an aggressively shrunk typed
172 : : * descriptor can be safely regrown and then used as a concatenation
173 : : * source. The first nested test proves that RELEASE_UNUSED really
174 : : * returns spare slab capacity while keeping the surviving prefix
175 : : * intact. The second nested test appends the regrown source to a
176 : : * five-point destination and verifies the complete thirteen-point
177 : : * result, so overwrites, missing source elements, and ordering bugs
178 : : * are visible
179 : : *
180 : : * Groups the typed-data checks that prove RELEASE_UNUSED can physically
181 : : * reduce a descriptor's reserve without corrupting the surviving prefix,
182 : : * and that m_concat_data can append a regrown typed source without
183 : : * overwriting the destination
184 : : *
185 : : * @return Return describing success or failure
186 : : */
187 : 1 : Return test_libmem_0012(void)
188 : : {
189 : 1 : INITTEST;
190 : :
191 : : /* Keep the physical shrink/regrow contract separate from the concat contract */
192 : 1 : TEST(test_libmem_0012_1,"RELEASE_UNUSED shrink across a slab boundary preserves the typed point prefix");
193 : 1 : TEST(test_libmem_0012_2,"m_concat_data appends a regrown typed point source after the destination prefix");
194 : :
195 : 1 : RETURN_STATUS;
196 : : }
|