GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/points/AttributeArrayString.cc
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 133 136 97.8%
Functions: 26 27 96.3%
Branches: 80 118 67.8%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @file points/AttributeArrayString.cc
5
6 #include "AttributeArrayString.h"
7
8 #include <openvdb/Metadata.h>
9 #include <openvdb/MetaMap.h>
10
11 #include <tbb/parallel_sort.h>
12
13 #include <string>
14
15 namespace openvdb {
16 OPENVDB_USE_VERSION_NAMESPACE
17 namespace OPENVDB_VERSION_NAME {
18 namespace points {
19
20
21 namespace {
22
23 1510193 Name getStringKey(const Index index)
24 {
25
1/2
✓ Branch 2 taken 1510193 times.
✗ Branch 3 not taken.
3020386 return "string:" + std::to_string(index - 1);
26 }
27
28 } // namespace
29
30
31 ////////////////////////////////////////
32
33
34 // StringMetaCache implementation
35
36
37
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 StringMetaCache::StringMetaCache(const MetaMap& metadata)
38 {
39
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 this->reset(metadata);
40 1 }
41
42
43 36895 void StringMetaCache::insert(const Name& key, Index index)
44 {
45 36895 mCache[key] = index;
46 36895 }
47
48
49 4263 void StringMetaCache::reset(const MetaMap& metadata)
50 {
51 mCache.clear();
52
53 // populate the cache
54
55
2/2
✓ Branch 0 taken 26770 times.
✓ Branch 1 taken 4263 times.
31033 for (auto it = metadata.beginMeta(), itEnd = metadata.endMeta(); it != itEnd; ++it) {
56
1/2
✓ Branch 0 taken 26770 times.
✗ Branch 1 not taken.
26770 const Name& key = it->first;
57 const Metadata::Ptr& meta = it->second;
58
59 // attempt to cast metadata to StringMetadata
60
1/2
✓ Branch 0 taken 26770 times.
✗ Branch 1 not taken.
26770 const StringMetadata* stringMeta = dynamic_cast<StringMetadata*>(meta.get());
61
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 26769 times.
26770 if (!stringMeta) continue;
62
63 // string attribute metadata must have a key that starts "string:"
64
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 26768 times.
26769 if (key.compare(0, 7, "string:") != 0) continue;
65
66 // remove "string:" and cast to Index
67 26768 Index index = 1 + static_cast<Index>(
68 53536 std::stoul(key.substr(7, key.size() - 7)));
69
70 // add to the cache
71 26768 this->insert(stringMeta->value(), index);
72 }
73 4263 }
74
75
76 ////////////////////////////////////////
77
78 // StringMetaInserter implementation
79
80
81 845 StringMetaInserter::StringMetaInserter(MetaMap& metadata)
82 : mMetadata(metadata)
83 , mIdBlocks()
84
1/2
✓ Branch 2 taken 845 times.
✗ Branch 3 not taken.
845 , mCache()
85 {
86 // populate the cache
87
1/2
✓ Branch 1 taken 845 times.
✗ Branch 2 not taken.
845 resetCache();
88 845 }
89
90
91 3 bool StringMetaInserter::hasKey(const Name& key) const
92 {
93 3 return mCache.map().find(key) != mCache.map().end();
94 }
95
96
97 3 bool StringMetaInserter::hasIndex(Index index) const
98 {
99 6 return bool(mMetadata[getStringKey(index)]);
100 }
101
102
103 1020315 Index StringMetaInserter::insert(const Name& name, Index hint)
104 {
105 using IterT = IndexPairArray::iterator;
106
107 // if name already exists, return the index
108
109 const auto& cacheMap = mCache.map();
110
2/2
✓ Branch 0 taken 1010189 times.
✓ Branch 1 taken 10126 times.
1020315 auto it = cacheMap.find(name);
111
2/2
✓ Branch 0 taken 1010189 times.
✓ Branch 1 taken 10126 times.
1020315 if (it != cacheMap.end()) {
112 1010189 return it->second;
113 }
114
115 Index index = 1;
116
117 Name hintKey;
118 bool canUseHint = false;
119
120 // hint must be non-zero to have been requested
121
122
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10122 times.
10126 if (hint > Index(0)) {
123
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 hintKey = getStringKey(hint);
124 // check if hint is already in use
125
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
8 if (!bool(mMetadata[hintKey])) {
126 canUseHint = true;
127 index = hint;
128 }
129 }
130
131 // look through the id blocks for hint or index
132
133 IterT iter = mIdBlocks.begin();
134
2/2
✓ Branch 0 taken 10073 times.
✓ Branch 1 taken 10121 times.
20194 for (; iter != mIdBlocks.end(); ++iter) {
135 10073 const Index start = iter->first;
136 10073 const Index end = start + iter->second;
137
138
2/2
✓ Branch 0 taken 10068 times.
✓ Branch 1 taken 5 times.
10073 if (index < start || index >= end) break;
139
1/2
✓ Branch 0 taken 10068 times.
✗ Branch 1 not taken.
10068 if (!canUseHint) index = end;
140 }
141
142 // index now holds the next valid index. if it's 1 (the beginning
143 // iterator) no initial block exists - add it
144
145 IterT prevIter;
146
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 10067 times.
10126 if (iter == mIdBlocks.begin()) {
147
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 prevIter = mIdBlocks.emplace(iter, 1, 1);
148 59 iter = std::next(prevIter);
149 }
150 else {
151 // accumulate the id block size where the next index is going
152 10067 prevIter = std::prev(iter);
153 10067 prevIter->second++;
154 }
155
156 // see if this block and the next block can be compacted
157
158
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10121 times.
10126 if (iter != mIdBlocks.end() &&
159
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 prevIter->second + 1 == iter->first) {
160 2 prevIter->second += iter->second;
161 2 mIdBlocks.erase(iter);
162 }
163
164 // insert into metadata
165
166
1/2
✓ Branch 1 taken 10126 times.
✗ Branch 2 not taken.
10126 const Name key = getStringKey(index);
167
2/4
✓ Branch 1 taken 10126 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10126 times.
✗ Branch 5 not taken.
20252 mMetadata.insertMeta(key, StringMetadata(name));
168
169 // update the cache
170
171
1/2
✓ Branch 1 taken 10126 times.
✗ Branch 2 not taken.
10126 mCache.insert(name, index);
172
173 return index;
174 }
175
176
177 850 void StringMetaInserter::resetCache()
178 {
179 850 mCache.reset(mMetadata);
180 850 mIdBlocks.clear();
181
182 std::vector<Index> stringIndices;
183
1/2
✓ Branch 1 taken 850 times.
✗ Branch 2 not taken.
850 stringIndices.reserve(mCache.size());
184
185
2/2
✓ Branch 0 taken 779 times.
✓ Branch 1 taken 71 times.
850 if (mCache.empty()) return;
186
187 const auto& cacheMap = mCache.map();
188
189
2/2
✓ Branch 0 taken 10117 times.
✓ Branch 1 taken 71 times.
10188 for (auto it = cacheMap.cbegin(); it != cacheMap.cend(); ++it) {
190 10117 const Index index = it->second;
191
192
1/2
✓ Branch 1 taken 10117 times.
✗ Branch 2 not taken.
10117 stringIndices.emplace_back(index);
193 }
194
195 71 tbb::parallel_sort(stringIndices.begin(), stringIndices.end());
196
197 // bucket string indices
198
199 71 Index key = stringIndices.front();
200 71 Index size = 0;
201
202 // For each id, see if it's adjacent id is sequentially increasing and continue to
203 // track how many are until we find a value that isn't. Store the start and length
204 // of each of these blocks. For example, the following container could be created
205 // consisting of 3 elements:
206 // key -> size
207 // -------------
208 // 7 -> 1000 (values 7->1007)
209 // 1020 -> 5 (values 1020->1025)
210 // 2013 -> 30 (values 2013->2043)
211 // Note that the end value is exclusive (values 1007, 1025 and 2043 do not exist
212 // given the above example)
213
214
2/2
✓ Branch 0 taken 10117 times.
✓ Branch 1 taken 71 times.
10188 for (const Index id : stringIndices) {
215
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10116 times.
10117 if (key + size != id) {
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 assert(size > 0);
217
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 mIdBlocks.emplace_back(key, size);
218 1 size = 0;
219 1 key = id;
220 }
221 10117 ++size;
222 }
223
224 // add the last block
225
1/2
✓ Branch 1 taken 71 times.
✗ Branch 2 not taken.
71 mIdBlocks.emplace_back(key, size);
226 }
227
228
229 ////////////////////////////////////////
230
231 // StringAttributeHandle implementation
232
233
234 StringAttributeHandle::Ptr
235 StringAttributeHandle::create(const AttributeArray& array, const MetaMap& metadata, const bool preserveCompression)
236 {
237 return std::make_shared<StringAttributeHandle>(array, metadata, preserveCompression);
238 }
239
240
241 9233 StringAttributeHandle::StringAttributeHandle(const AttributeArray& array,
242 const MetaMap& metadata,
243 9233 const bool preserveCompression)
244 : mHandle(array, preserveCompression)
245
1/2
✓ Branch 2 taken 9232 times.
✗ Branch 3 not taken.
9233 , mMetadata(metadata)
246 {
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9232 times.
9232 if (!isString(array)) {
248 OPENVDB_THROW(TypeError, "Cannot create a StringAttributeHandle for an attribute array that is not a string.");
249 }
250 9232 }
251
252
253
2/2
✓ Branch 1 taken 1500044 times.
✓ Branch 2 taken 1 times.
1500045 Name StringAttributeHandle::get(Index n, Index m) const
254 {
255 Name name;
256
2/2
✓ Branch 1 taken 1500044 times.
✓ Branch 2 taken 1 times.
1500045 this->get(name, n, m);
257 1500044 return name;
258 }
259
260
261 1500272 void StringAttributeHandle::get(Name& name, Index n, Index m) const
262 {
263 1500272 Index index = mHandle.get(n, m);
264
265 // index zero is reserved for an empty string
266
267
2/2
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 1500060 times.
1500272 if (index == 0) {
268 212 name = "";
269 212 return;
270 }
271
272 1500060 const Name key = getStringKey(index);
273
274 // key is assumed to exist in metadata
275
276
1/2
✓ Branch 1 taken 1500060 times.
✗ Branch 2 not taken.
1500060 openvdb::StringMetadata::ConstPtr meta = mMetadata.getMetadata<StringMetadata>(key);
277
278
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1500059 times.
1500060 if (!meta) {
279
2/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
5 OPENVDB_THROW(LookupError, "String attribute cannot be found with index - \"" << index << "\".");
280 }
281
282 name = meta->value();
283 }
284
285 225 const AttributeArray& StringAttributeHandle::array() const
286 {
287 225 return mHandle.array();
288 }
289
290
291 ////////////////////////////////////////
292
293 // StringAttributeWriteHandle implementation
294
295 StringAttributeWriteHandle::Ptr
296 148 StringAttributeWriteHandle::create(AttributeArray& array, const MetaMap& metadata, const bool expand)
297 {
298 148 return std::make_shared<StringAttributeWriteHandle>(array, metadata, expand);
299 }
300
301
302 3409 StringAttributeWriteHandle::StringAttributeWriteHandle(AttributeArray& array,
303 const MetaMap& metadata,
304 3409 const bool expand)
305 : StringAttributeHandle(array, metadata, /*preserveCompression=*/ false)
306
1/2
✓ Branch 2 taken 3409 times.
✗ Branch 3 not taken.
3409 , mWriteHandle(array, expand)
307 {
308 // populate the cache
309
1/2
✓ Branch 1 taken 3409 times.
✗ Branch 2 not taken.
3409 resetCache();
310 3409 }
311
312
313 2 void StringAttributeWriteHandle::expand(bool fill)
314 {
315 2 mWriteHandle.expand(fill);
316 2 }
317
318
319 1 void StringAttributeWriteHandle::collapse()
320 {
321 // zero is used for an empty string
322 1 mWriteHandle.collapse(0);
323 1 }
324
325
326 186 void StringAttributeWriteHandle::collapse(const Name& name)
327 {
328 186 Index index = getIndex(name);
329 186 mWriteHandle.collapse(index);
330 186 }
331
332
333 2909 bool StringAttributeWriteHandle::compact()
334 {
335 2909 return mWriteHandle.compact();
336 }
337
338
339 1 void StringAttributeWriteHandle::fill(const Name& name)
340 {
341 1 Index index = getIndex(name);
342 1 mWriteHandle.fill(index);
343 1 }
344
345
346 220 void StringAttributeWriteHandle::set(Index n, const Name& name)
347 {
348 220 Index index = getIndex(name);
349 219 mWriteHandle.set(n, /*stride*/0, index);
350 219 }
351
352
353 1000021 void StringAttributeWriteHandle::set(Index n, Index m, const Name& name)
354 {
355 1000021 Index index = getIndex(name);
356 1000021 mWriteHandle.set(n, m, index);
357 1000021 }
358
359
360 3411 void StringAttributeWriteHandle::resetCache()
361 {
362 3411 mCache.reset(mMetadata);
363 3411 }
364
365
366 210 AttributeArray& StringAttributeWriteHandle::array()
367 {
368 210 return mWriteHandle.array();
369 }
370
371
372
2/2
✓ Branch 0 taken 5196 times.
✓ Branch 1 taken 22 times.
5218 bool StringAttributeWriteHandle::contains(const Name& name) const
373 {
374 // empty strings always have an index at index zero
375
2/2
✓ Branch 0 taken 5196 times.
✓ Branch 1 taken 22 times.
5218 if (name.empty()) return true;
376 const auto& cacheMap = mCache.map();
377 5196 return cacheMap.find(name) != cacheMap.end();
378 }
379
380
381
2/2
✓ Branch 0 taken 1000407 times.
✓ Branch 1 taken 21 times.
1000428 Index StringAttributeWriteHandle::getIndex(const Name& name) const
382 {
383 // zero used for an empty string
384
2/2
✓ Branch 0 taken 1000407 times.
✓ Branch 1 taken 21 times.
1000428 if (name.empty()) return Index(0);
385
386 const auto& cacheMap = mCache.map();
387
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1000406 times.
1000407 auto it = cacheMap.find(name);
388
389
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1000406 times.
1000407 if (it == cacheMap.end()) {
390
2/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
5 OPENVDB_THROW(LookupError, "String does not exist in Metadata, insert it and reset the cache - \"" << name << "\".");
391 }
392
393 1000406 return it->second;
394 }
395
396
397 ////////////////////////////////////////
398
399
400 } // namespace points
401 } // namespace OPENVDB_VERSION_NAME
402 } // namespace openvdb
403