GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/packed_arrays.h
Date: 2026-09-07 02:36:43
Exec Total Coverage
Lines: 24 32 75.0%
Functions: 6 8 75.0%
Branches: 6 18 33.3%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_BASIC_PACKED_ARRAYS
41 #define GEOGRAM_BASIC_PACKED_ARRAYS
42
43 #include <geogram/basic/common.h>
44 #include <geogram/basic/numeric.h>
45 #include <geogram/basic/assert.h>
46 #include <geogram/basic/process.h>
47
48 /**
49 * \file geogram/basic/packed_arrays.h
50 * \brief Efficient storage for array of arrays
51 */
52
53 namespace GEO {
54
55 /**
56 * \brief Efficient storage for array of arrays.
57 * \details
58 * PackedArray is not a two-dimensional array, it is an array of values
59 * where each value is itself an array of \c GEO::index_t.
60 * PackedArrays provides functions for efficiently setting and getting
61 * arrays by \b value (set_array() and get_array()).
62 *
63 * PackedArrays tries to be as memory efficient as possible:
64 * - each sub-array is configured with a default capacity allowing to
65 * allocate a single fixed size memory area for all sub-arrays
66 * - sub-arrays can be resized individually using an overflow area by
67 * calling resize_array().
68 *
69 * The idea behind PackedArrays is to be able to efficiently operate in a
70 * multi-threaded environment while limiting the locking time of the
71 * sub-arrays.
72 *
73 * A classical read operation mode would be:
74 * - lock a sub-array
75 * - read the sub-array by value
76 * - unlock the sub-array
77 * - access sub-array elements
78 *
79 * A classical write operation mode would be:
80 * - prepare or modify sub-array elements
81 * - lock a sub-array
82 * - write sub-the array by value
83 * - unlock the sub-array
84 *
85 * PackedArrays use SpinLockArray to lock/unlock sub-arrays in a
86 * multi-threaded environment.
87 */
88 class GEOGRAM_API PackedArrays {
89 public:
90 /**
91 * \brief Creates a new empty packed array
92 */
93 PackedArrays();
94
95 /**
96 * \brief Deletes a packed array
97 */
98 ~PackedArrays();
99
100 /**
101 * \brief Checks the thread-safety mode
102 * \retval true if the array is configured to be thread-safe
103 * \retval false otherwise
104 */
105 bool thread_safe() const {
106 return thread_safe_;
107 }
108
109 /**
110 * \brief Sets the thread-safety mode
111 * \param[in] flag configures the array to be thread-safe if \c true.
112 */
113 void set_thread_safe(bool flag);
114
115 /**
116 * \brief Initializes a packed array
117 * \details This allocates storage for storing \p nb_arrays with a
118 * capacity of \p Z1_block_size elements of type \c
119 * GEO::index_t. If parameter static_mode is \c true, the
120 * overflow area is disabled and arrays are constrained to a maximum
121 * of \p Z1_block_size elements.
122 * \param[in] nb_arrays number of arrays to allocate
123 * \param[in] Z1_block_size default capacity of the arrays (in number
124 * of elements)
125 * \param[in] static_mode specifies whether the array is static mode
126 */
127 void init(
128 index_t nb_arrays,
129 index_t Z1_block_size,
130 bool static_mode = false
131 );
132
133 /**
134 * \brief Clears the packed array
135 * \details This frees allocated storage and resets the array as being
136 * default-constructed. Call function init() with different parameters
137 * to use it again.
138 */
139 void clear();
140
141 /**
142 * \brief Get the number of arrays
143 * \return the number of arrays in this packed array
144 */
145 461 index_t nb_arrays() const {
146 461 return nb_arrays_;
147 }
148
149 /**
150 * \brief Gets the size of a sub-array
151 * \details Returns the number of actual elements in the sub-array at
152 * index \p array_index. This corresponds to the number of elements in
153 * the last sub-array set by set_array(). This differs from the
154 * default sub-array capacity used to preallocate the storage for the
155 * packed-array.
156 * \param[in] array_index index of the sub-array
157 * \return the actual size of the sub-array elements
158 */
159 6794832 index_t array_size(index_t array_index) const {
160
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 6794832 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
6794832 geo_debug_assert(array_index < nb_arrays_);
161 6794832 return Z1_[array_index * Z1_stride_];
162 }
163
164 /**
165 * \brief Gets a sub-array as a vector
166 * \details
167 * Copies the elements of the sub-array at index \p array_index to the
168 * vector \p array.
169 *
170 * The operation is atomic if parameter \p lock is \c true (the
171 * default) and the array is configured in thread-safety mode.
172 *
173 * \param[in] array_index the index of the sub-array
174 * \param[out] array the output vector
175 * \param[in] lock specifies whether the operation is atomic
176 */
177 4137273 void get_array(
178 index_t array_index, vector<index_t>& array, bool lock = true
179 ) const {
180
1/2
✓ Branch 0 taken 4137273 times.
✗ Branch 1 not taken.
4137273 if(lock) {
181 4137273 lock_array(array_index);
182 }
183 4137273 array.resize(array_size(array_index));
184
1/2
✓ Branch 1 taken 4137273 times.
✗ Branch 2 not taken.
4137273 if(array.size() != 0) {
185 4137273 get_array(array_index, &array[0], false);
186 }
187
1/2
✓ Branch 0 taken 4137273 times.
✗ Branch 1 not taken.
4137273 if(lock) {
188 4137273 unlock_array(array_index);
189 }
190 4137273 }
191
192 /**
193 * \brief Gets a sub-array
194 * \details
195 * Copies the elements of the sub-array at index \p array_index to the
196 * array \p array. The caller must make sure that \p array has enough
197 * space for storing \c array_size(array_index) elements.
198 *
199 * The operation is atomic if parameter \p lock is \c true (the
200 * default) and the array is configured in thread-safety mode.
201 *
202 * \param[in] array_index the index of the sub-array
203 * \param[out] array the output array of at least \p array_size
204 * elements.
205 * \param[in] lock specifies whether the operation is atomic
206 */
207 void get_array(
208 index_t array_index, index_t* array, bool lock = true
209 ) const;
210
211 /**
212 * \brief Sets a sub-array
213 * \details
214 * Copies the first \p array_size elements of the array \p
215 * array_elements to the sub-array at index \p array_index. If \p
216 * array_size is different than the sub-array size, the sub-array is
217 * resized (see resize_array()).
218 *
219 * If the packed array is in static mode, setting a sub-array with a
220 * size greater than the original sub-array capacity makes the
221 * function abort().
222 *
223 * The \p lock is \c true (the default) and the array is configured in
224 * thread-safety mode.
225 *
226 * \param[in] array_index the index of the sub-array
227 * \param[in] array_size the number of elements to copy
228 * \param[in] array_elements an array of at least \p array_size elements
229 * \param[in] lock specifies whether the operation is atomic
230 */
231 void set_array(
232 index_t array_index,
233 index_t array_size, const index_t* array_elements,
234 bool lock = true
235 );
236
237 /**
238 * \brief Sets a sub-array from a vector
239 * \details
240 * Copies all the elements in vector \p array to the sub-array at
241 * index \p array_index. If the number of elements in \p vector is
242 * different than the sub-array size, the sub-array is resized (see
243 * resize_array()).
244 *
245 * If the packed array is in static mode, setting a sub-array with a
246 * size greater than the original sub-array capacity makes the
247 * function abort().
248 *
249 * The operation is atomic if parameter \p lock is \c true (the
250 * default) and the array is configured in thread-safety mode.
251 *
252 * \param[in] array_index the index of the sub-array
253 * \param[in] array an array of at least \p array_size elements
254 * \param[in] lock specifies whether the operation is atomic
255 */
256 void set_array(
257 index_t array_index,
258 const vector<index_t>& array,
259 bool lock = true
260 ) {
261 if(array.size() == 0) {
262 set_array(array_index, 0, nullptr, lock);
263 } else {
264 set_array(
265 array_index, index_t(array.size()), &array[0], lock
266 );
267 }
268 }
269
270 /**
271 * \brief Resizes a sub-array
272 * \details
273 * Reallocates the sub-array at index \p array_index to contain \p
274 * array_size elements:
275 * - if \p array_size is greater than the original sub-array capacity,
276 * space is allocated in the overflow area to contain the extra
277 * elements,
278 * - if \p array_size is smaller than the original sub-array capacity,
279 * any space allocated for the sub-array in the overflow area is
280 * released.
281 *
282 * The operation is atomic if parameter \p lock is \c true (the
283 * default) and the array is configured in thread-safety mode.
284 *
285 * \param[in] array_index the index of the sub-array
286 * \param[in] array_size the new size of the sub-array
287 * \param[in] lock specifies whether the operation is atomic
288 */
289 void resize_array(
290 index_t array_index, index_t array_size, bool lock
291 );
292
293 /**
294 * \brief Locks a sub-array
295 * \details This gives the calling thread exclusive access to the
296 * sub-array at index \p array_index. Other threads calling this
297 * function for the same sub-array will block until the blocker thread
298 * calls unlock_array().
299 * \param[in] array_index the index of the sub-array
300 */
301 4163070 void lock_array(index_t array_index) const {
302
1/2
✓ Branch 0 taken 4163070 times.
✗ Branch 1 not taken.
4163070 if(thread_safe_) {
303 4163070 Z1_spinlocks_.acquire_spinlock(array_index);
304 }
305 4163070 }
306
307 /**
308 * \brief Unlocks a sub-array
309 * \details Releases the lock on the sub-array at index \p
310 * array_index. This gives access to the sub-array to any waiting
311 * thread.
312 * \param[in] array_index the index of the sub-array
313 */
314 4163070 void unlock_array(index_t array_index) const {
315
1/2
✓ Branch 0 taken 4163070 times.
✗ Branch 1 not taken.
4163070 if(thread_safe_) {
316 4163070 Z1_spinlocks_.release_spinlock(array_index);
317 }
318 4163070 }
319
320 /**
321 * \brief Displays array statistics
322 * \details This prints statistics about memory occupation in the
323 * main allocation area plus statistics about extra space allocated in
324 * the overflow area.
325 */
326 void show_stats();
327
328 protected:
329 /**
330 * \brief Checks if the array is in static mode
331 * \retval true if the array is in static mode
332 * \retval false otherwise
333 */
334 95148 bool static_mode() const {
335 95148 return ZV_ == nullptr;
336 }
337
338 private:
339 /** Forbid copy constructor */
340 PackedArrays(const PackedArrays& rhs);
341
342 /** Forbid assignment operator */
343 PackedArrays& operator= (const PackedArrays& rhs);
344
345 private:
346 index_t nb_arrays_;
347 index_t Z1_block_size_;
348 index_t Z1_stride_;
349 index_t* Z1_;
350 index_t** ZV_;
351 bool thread_safe_;
352 mutable Process::SpinLockArray Z1_spinlocks_;
353 };
354 }
355
356 #endif
357