1
#include "PreCompiled.h"
3
#include <unordered_map>
10
#include "ElementNamingUtils.h"
12
#include "App/Application.h"
13
#include "Base/Console.h"
15
#include "DocumentObject.h"
17
#include <boost/algorithm/string/classification.hpp>
18
#include <boost/algorithm/string/split.hpp>
21
FC_LOG_LEVEL_INIT("ElementMap", true, 2);// NOLINT
27
// Because the existence of hierarchical element maps, for the same document
28
// we may store an element map more than once in multiple objects. And because
29
// we may want to support partial loading, we choose to tolerate such redundancy
32
// In order to not waste memory space when the file is loaded, we use the
33
// following two maps to assign a one-time id for each unique element map. The
34
// id will be saved together with the element map.
36
// When restoring, we'll read back the id and lookup for an existing element map
37
// with the same id, and skip loading the current map if one is found.
39
// TODO: Note that the same redundancy can be found when saving OCC shapes,
40
// because we currently save shapes for each object separately. After restoring,
41
// any shape sharing is lost. But again, we do want to keep separate shape files
42
// because of partial loading. The same technique used here can be applied to
43
// restore shape sharing.
44
static std::unordered_map<const ElementMap*, unsigned> _elementMapToId;
45
static std::unordered_map<unsigned, ElementMapPtr> _idToElementMap;
48
void ElementMap::init()
53
::App::GetApplication().signalStartSaveDocument.connect(
54
[](const ::App::Document&, const std::string&) {
55
_elementMapToId.clear();
57
::App::GetApplication().signalFinishSaveDocument.connect(
58
[](const ::App::Document&, const std::string&) {
59
_elementMapToId.clear();
61
::App::GetApplication().signalStartRestoreDocument.connect([](const ::App::Document&) {
62
_idToElementMap.clear();
64
::App::GetApplication().signalFinishRestoreDocument.connect([](const ::App::Document&) {
65
_idToElementMap.clear();
70
ElementMap::ElementMap()
76
void ElementMap::beforeSave(const ::App::StringHasherRef& hasherRef) const
78
unsigned& id = _elementMapToId[this];
80
id = _elementMapToId.size();
84
for (auto& indexedName : this->indexedNames) {
85
for (const MappedNameRef& mappedName : indexedName.second.names) {
86
for (const MappedNameRef* ref = &mappedName; ref; ref = ref->next.get()) {
87
for (const ::App::StringIDRef& sid : ref->sids) {
88
if (sid.isFromSameHasher(hasherRef)) {
94
for (auto& childPair : indexedName.second.children) {
95
if (childPair.second.elementMap) {
96
childPair.second.elementMap->beforeSave(hasherRef);
98
for (auto& sid : childPair.second.sids) {
99
if (sid.isFromSameHasher(hasherRef)) {
107
void ElementMap::save(std::ostream& stream, int index,
108
const std::map<const ElementMap*, int>& childMapSet,
109
const std::map<QByteArray, int>& postfixMap) const
111
stream << "\nElementMap " << index << ' ' << this->_id << ' ' << this->indexedNames.size()
114
for (auto& indexedName : this->indexedNames) {
115
stream << '\n' << indexedName.first << '\n';
117
stream << "\nChildCount " << indexedName.second.children.size() << '\n';
118
for (auto& vv : indexedName.second.children) {
119
auto& child = vv.second;
121
if (child.elementMap) {
122
auto it = childMapSet.find(child.elementMap.get());
123
if (it == childMapSet.end() || it->second == 0) {
124
FC_ERR("Invalid child element map");// NOLINT
127
mapIndex = it->second;
130
stream << child.indexedName.getIndex() << ' ' << child.offset << ' ' << child.count
131
<< ' ' << child.tag << ' ' << mapIndex << ' ';
132
stream.write(child.postfix.constData(), child.postfix.size());
133
stream << ' ' << '0';
134
for (auto& sid : child.sids) {
135
if (sid.isMarked()) {
136
stream << '.' << sid.value();
142
stream << "\nNameCount " << indexedName.second.names.size() << '\n';
143
if (indexedName.second.names.empty()) {
147
boost::io::ios_flags_saver ifs(stream);
150
for (auto& dequeueOfMappedNameRef : indexedName.second.names) {
151
for (auto ref = &dequeueOfMappedNameRef; ref; ref = ref->next.get()) {
156
::App::StringID::IndexID prefixID {};
158
IndexedName idx(ref->name.dataBytes());
159
bool printName = true;
161
auto key = QByteArray::fromRawData(idx.getType(),
162
static_cast<int>(qstrlen(idx.getType())));
163
auto it = postfixMap.find(key);
164
if (it != postfixMap.end()) {
165
stream << ':' << it->second << '.' << idx.getIndex();
170
prefixID = ::App::StringID::fromString(ref->name.dataBytes());
171
if (prefixID.id != 0) {
172
for (auto& sid : ref->sids) {
173
if (sid.isMarked() && sid.value() == prefixID.id) {
175
stream.write(ref->name.dataBytes().constData(),
176
ref->name.dataBytes().size());
188
stream.write(ref->name.dataBytes().constData(), ref->name.dataBytes().size());
191
const QByteArray& postfix = ref->name.postfixBytes();
192
if (postfix.isEmpty()) {
196
auto it = postfixMap.find(postfix);
197
assert(it != postfixMap.end());
198
stream << '.' << it->second;
200
for (auto& sid : ref->sids) {
201
if (sid.isMarked() && sid.value() != prefixID.id) {
202
stream << '.' << sid.value();
211
stream << "\nEndMap\n";
214
void ElementMap::save(std::ostream& stream) const
216
std::map<const ElementMap*, int> childMapSet;
217
std::vector<const ElementMap*> childMaps;
218
std::map<QByteArray, int> postfixMap;
219
std::vector<QByteArray> postfixes;
221
collectChildMaps(childMapSet, childMaps, postfixMap, postfixes);
223
stream << this->_id << " PostfixCount " << postfixes.size() << '\n';
224
for (auto& postfix : postfixes) {
225
stream.write(postfix.constData(), postfix.size());
229
stream << "\nMapCount " << childMaps.size() << '\n';
230
for (auto& elementMap : childMaps) {
231
elementMap->save(stream, ++index, childMapSet, postfixMap);
235
ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream& stream)
237
const char* msg = "Invalid element map";
242
if (!(stream >> id >> tmp >> count) || tmp != "PostfixCount") {
243
FC_THROWM(Base::RuntimeError, msg);// NOLINT
246
auto& map = _idToElementMap[id];
251
std::vector<std::string> postfixes;
252
postfixes.reserve(count);
253
for (int i = 0; i < count; ++i) {
254
postfixes.emplace_back();
255
stream >> postfixes.back();
258
std::vector<ElementMapPtr> childMaps;
260
if (!(stream >> tmp >> count) || tmp != "MapCount" || count == 0) {
261
FC_THROWM(Base::RuntimeError, msg);// NOLINT
263
childMaps.reserve(count - 1);
264
for (int i = 0; i < count - 1; ++i) {
266
std::make_shared<ElementMap>()->restore(hasherRef, stream, childMaps, postfixes));
269
return restore(hasherRef, stream, childMaps, postfixes);
272
ElementMapPtr ElementMap::restore(::App::StringHasherRef hasherRef, std::istream& stream,
273
std::vector<ElementMapPtr>& childMaps,
274
const std::vector<std::string>& postfixes)
276
const char* msg = "Invalid element map";
277
const int hexBase {16};
278
const int decBase {10};
283
if (!(stream >> tmp >> index >> id >> typeCount) || tmp != "ElementMap") {
284
FC_THROWM(Base::RuntimeError, msg);// NOLINT
287
auto& map = _idToElementMap[id];
289
while (tmp != "EndMap") {
290
if (!std::getline(stream, tmp)) {
291
FC_THROWM(Base::RuntimeError, "unexpected end of child element map");// NOLINT
297
const char* hasherWarn = nullptr;
298
const char* hasherIDWarn = nullptr;
299
const char* postfixWarn = nullptr;
300
const char* childSIDWarn = nullptr;
301
std::vector<std::string> tokens;
303
for (int i = 0; i < typeCount; ++i) {
305
if (!(stream >> tmp)) {
306
FC_THROWM(Base::RuntimeError, "missing element type");// NOLINT
308
IndexedName idx(tmp.c_str(), 1);
310
if (!(stream >> tmp >> outerCount) || tmp != "ChildCount") {
311
FC_THROWM(Base::RuntimeError, "missing element child count");// NOLINT
314
auto& indices = this->indexedNames[idx.getType()];
315
for (int j = 0; j < outerCount; ++j) {
321
if (!(stream >> cIndex >> offset >> count >> tag >> mapIndex >> tmp)) {
322
FC_THROWM(Base::RuntimeError, "Invalid element child");// NOLINT
325
FC_THROWM(Base::RuntimeError, "Invalid element child index");// NOLINT
328
FC_THROWM(Base::RuntimeError, "Invalid element child offset");// NOLINT
330
if (mapIndex >= index || mapIndex < 0 || mapIndex > (int)childMaps.size()) {
331
FC_THROWM(Base::RuntimeError, "Invalid element child map index");// NOLINT
333
auto& child = indices.children[cIndex + offset + count];
334
child.indexedName = IndexedName::fromConst(idx.getType(), cIndex);
335
child.offset = offset;
339
child.elementMap = childMaps[mapIndex - 1];
342
child.elementMap = nullptr;
344
child.postfix = tmp.c_str();
345
this->childElements[child.postfix].childMap = &child;
346
this->childElementSize += child.count;
348
if (!(stream >> tmp)) {
349
FC_THROWM(Base::RuntimeError, "Invalid element child string id");// NOLINT
353
boost::split(tokens, tmp, boost::is_any_of("."));
354
if (tokens.size() > 1) {
355
child.sids.reserve(static_cast<int>(tokens.size()) - 1);
356
for (unsigned k = 1; k < tokens.size(); ++k) {
357
// The element child string ID is saved as decimal
358
// instead of hex by accident. To simplify maintenance
359
// of backward compatibility, it is not corrected, and
360
// just restored as decimal here.
361
long childID = strtol(tokens[k].c_str(), nullptr, decBase);
362
auto sid = hasherRef->getID(childID);
364
childSIDWarn = "Missing element child string id";
367
child.sids.push_back(sid);
373
if (!(stream >> tmp >> outerCount) || tmp != "NameCount") {
374
FC_THROWM(Base::RuntimeError, "missing element name outerCount");// NOLINT
377
boost::io::ios_flags_saver ifs(stream);
380
indices.names.resize(outerCount);
381
for (int j = 0; j < outerCount; ++j) {
383
auto* ref = &indices.names[j];
386
if (!(stream >> tmp)) {
387
FC_THROWM(Base::RuntimeError, "Failed to read element name");// NOLINT
392
if (innerCount++ != 0) {
393
ref->next = std::make_unique<MappedNameRef>();
394
ref = ref->next.get();
397
boost::split(tokens, tmp, boost::is_any_of("."));
398
if (tokens.size() < 2) {
399
FC_THROWM(Base::RuntimeError, "Invalid element entry");// NOLINT
403
::App::StringID::IndexID prefixID {};
406
switch (tokens[0][0]) {
408
if (tokens.size() < 3) {
409
FC_THROWM(Base::RuntimeError, "Invalid element entry");// NOLINT
412
long elementNameIndex = strtol(tokens[0].c_str() + 1, nullptr, hexBase);
413
if (elementNameIndex <= 0 || elementNameIndex > (int)postfixes.size()) {
414
FC_THROWM(Base::RuntimeError, "Invalid element name index");// NOLINT
416
long elementIndex = strtol(tokens[1].c_str(), nullptr, hexBase);
417
ref->name = MappedName(
418
IndexedName::fromConst(postfixes[elementNameIndex - 1].c_str(),
419
static_cast<int>(elementIndex)));
423
ref->name = MappedName(tokens[0].c_str() + 1);
424
prefixID = ::App::StringID::fromString(ref->name.dataBytes());
427
ref->name = MappedName(tokens[0].c_str() + 1);
430
FC_THROWM(Base::RuntimeError, "Invalid element name marker");// NOLINT
433
if (tokens[offset] != "0") {
434
long postfixIndex = strtol(tokens[offset].c_str(), nullptr, hexBase);
435
if (postfixIndex <= 0 || postfixIndex > (int)postfixes.size()) {
436
postfixWarn = "Invalid element postfix index";
439
ref->name += postfixes[postfixIndex - 1];
443
this->mappedNames.emplace(ref->name, idx);
446
if (offset + 1 < (int)tokens.size()) {
447
hasherWarn = "No hasherRef";
452
ref->sids.reserve((tokens.size() - offset - 1 + prefixID.id) != 0U ? 1 : 0);
453
if (prefixID.id != 0) {
454
auto sid = hasherRef->getID(prefixID.id);
456
hasherIDWarn = "Missing element name prefix id";
459
ref->sids.push_back(sid);
462
for (int l = offset + 1; l < (int)tokens.size(); ++l) {
463
long readID = strtol(tokens[l].c_str(), nullptr, hexBase);
464
auto sid = hasherRef->getID(readID);
466
hasherIDWarn = "Invalid element name string id";
469
ref->sids.push_back(sid);
476
FC_WARN(hasherWarn);// NOLINT
479
FC_WARN(hasherIDWarn);// NOLINT
482
FC_WARN(postfixWarn);// NOLINT
485
FC_WARN(childSIDWarn);// NOLINT
488
if (!(stream >> tmp) || tmp != "EndMap") {
489
FC_THROWM(Base::RuntimeError, "unexpected end of child element map");// NOLINT
492
return shared_from_this();
495
MappedName ElementMap::addName(MappedName& name, const IndexedName& idx, const ElementIDRefs& sids,
496
bool overwrite, IndexedName* existing)
498
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
499
if (name.find("#") >= 0 && name.findTagInElementName() < 0) {
500
FC_ERR("missing tag postfix " << name);// NOLINT
507
auto ret = mappedNames.insert(std::make_pair(name, idx));
508
if (ret.second) { // element just inserted did not exist yet in the map
509
ret.first->first.compact();// FIXME see MappedName.cpp
510
mappedRef(idx).append(ret.first->first, sids);
511
FC_TRACE(idx << " -> " << name);// NOLINT
512
return ret.first->first;
514
if (ret.first->second == idx) {
515
FC_TRACE("duplicate " << idx << " -> " << name);// NOLINT
516
return ret.first->first;
520
*existing = ret.first->second;
525
erase(ret.first->first);
529
void ElementMap::addPostfix(const QByteArray& postfix, std::map<QByteArray, int>& postfixMap,
530
std::vector<QByteArray>& postfixes)
532
if (postfix.isEmpty()) {
535
auto res = postfixMap.insert(std::make_pair(postfix, 0));
537
postfixes.push_back(postfix);
538
res.first->second = (int)postfixes.size();
542
MappedName ElementMap::setElementName(const IndexedName& element, const MappedName& name,
543
long masterTag, const ElementIDRefs* sid, bool overwrite)
546
throw Base::ValueError("Invalid input");
553
for (int i = 0, count = name.size(); i < count; ++i) {
554
char check = name[i];
555
if (check == '.' || (std::isspace((int)check) != 0)) {
556
FC_THROWM(Base::RuntimeError, "Illegal character in mapped name: " << name);// NOLINT
559
for (const char* readChar = element.getType(); *readChar != 0; ++readChar) {
560
char check = *readChar;
561
if (check == '.' || (std::isspace((int)check) != 0)) {
562
FC_THROWM(Base::RuntimeError,// NOLINT
563
"Illegal character in element name: " << element);
567
// Originally in ComplexGeoData::setElementName
568
// LinkStable/src/App/ComplexGeoData.cpp#L1631
569
// No longer possible after map separated in ElementMap.cpp
572
// resetElementMap(std::make_shared<ElementMap>());
579
std::ostringstream ss;
580
Data::MappedName mappedName(name);
582
IndexedName existing;
583
MappedName res = this->addName(mappedName, element, *sid, overwrite, &existing);
587
const int maxAttempts {100};
588
if (++i == maxAttempts) {
589
FC_ERR("unresolved duplicate element mapping '"// NOLINT
590
<< name << ' ' << element << '/' << existing);
596
mappedName = renameDuplicateElement(i, element, existing, name, _sid, masterTag);
604
// try to hash element name while preserving the source tag
605
void ElementMap::encodeElementName(char element_type, MappedName& name, std::ostringstream& ss,
606
ElementIDRefs* sids, long masterTag, const char* postfix,
607
long tag, bool forceTag) const
609
if (postfix && (postfix[0] != 0)) {
610
if (!boost::starts_with(postfix, ELEMENT_MAP_PREFIX)) {
611
ss << ELEMENT_MAP_PREFIX;
616
if (!forceTag && (ss.tellp() == 0)) {
617
if ((tag == 0) || tag == masterTag) {
620
name.findTagInElementName(&inputTag, nullptr, nullptr, nullptr, true);
621
if (inputTag == tag) {
625
else if ((tag == 0) || (!forceTag && tag == masterTag)) {
626
int pos = name.findTagInElementName(&inputTag, nullptr, nullptr, nullptr, true);
629
// About to encode the same tag used last time. This usually means
630
// the owner object is doing multistep modeling. Let's not
631
// recursively encode the same tag too many times. It will be a
632
// waste of memory, because the intermediate shapes has no
633
// corresponding objects, so no real value for history tracing.
635
// On the other hand, we still need to distinguish the original name
636
// from the input object from the element name of the intermediate
637
// shapes. So we limit ourselves to encode only one extra level
638
// using the same tag. In order to do that, we need to de-hash the
639
// previous level name, and check for its tag.
640
Data::MappedName mappedName(name, 0, pos);
641
Data::MappedName prev = dehashElementName(mappedName);
643
prev.findTagInElementName(&prevTag, nullptr, nullptr, nullptr, true);
644
if (prevTag == inputTag || prevTag == -inputTag) {
650
if (sids && this->hasher) {
651
name = hashElementName(name, *sids);
652
if (!forceTag && (tag == 0) && (ss.tellp() != 0)) {
656
if (forceTag || (tag != 0)) {
657
assert(element_type);
658
auto pos = ss.tellp();
659
boost::io::ios_flags_saver ifs(ss);
660
ss << POSTFIX_TAG << std::hex;
671
ss << ',' << element_type;
676
MappedName ElementMap::hashElementName(const MappedName& name, ElementIDRefs& sids) const
678
if (!this->hasher || !name) {
681
if (name.find(ELEMENT_MAP_PREFIX) < 0) {
684
App::StringIDRef sid = this->hasher->getID(name, sids);
685
const auto& related = sid.relatedIDs();
686
if (related == sids) {
693
for (auto& checkSID : sids) {
694
if (related.indexOf(checkSID) < 0) {
695
tmp.push_back(checkSID);
700
return MappedName(sid.toString());
703
MappedName ElementMap::dehashElementName(const MappedName& name) const
711
auto id = App::StringID::fromString(name.toRawBytes());
715
auto sid = this->hasher->getID(id);
717
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) {
718
FC_WARN("failed to find hash id " << id);// NOLINT
721
FC_LOG("failed to find hash id " << id);// NOLINT
725
if (sid.isHashed()) {
726
FC_LOG("cannot de-hash id " << id);// NOLINT
730
// sid.toString());// FIXME .toString() was missing in original function. is this correct?
731
FC_TRACE("de-hash " << name << " -> " << ret);// NOLINT
735
MappedName ElementMap::renameDuplicateElement(int index, const IndexedName& element,
736
const IndexedName& element2, const MappedName& name,
737
ElementIDRefs& sids, long masterTag) const
743
static std::random_device _RD;
744
static std::mt19937 _RGEN(_RD());
745
static std::uniform_int_distribution<> _RDIST(1, 10000);
749
std::ostringstream ss;
750
ss << ELEMENT_MAP_PREFIX << 'D' << std::hex << idx;
751
MappedName renamed(name);
752
encodeElementName(element.getType()[0], renamed, ss, &sids, masterTag);
753
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
754
FC_WARN("duplicate element mapping '"// NOLINT
755
<< name << " -> " << renamed << ' ' << element << '/' << element2);
760
void ElementMap::erase(const MappedName& name)
762
auto it = this->mappedNames.find(name);
763
if (it == this->mappedNames.end()) {
766
MappedNameRef* ref = findMappedRef(it->second);
771
this->mappedNames.erase(it);
774
void ElementMap::erase(const IndexedName& idx)
776
auto iter = this->indexedNames.find(idx.getType());
777
if (iter == this->indexedNames.end()) {
780
auto& indices = iter->second;
781
if (idx.getIndex() >= (int)indices.names.size()) {
784
auto& ref = indices.names[idx.getIndex()];
785
for (auto* nameRef = &ref; nameRef; nameRef = nameRef->next.get()) {
786
this->mappedNames.erase(nameRef->name);
791
unsigned long ElementMap::size() const
793
return mappedNames.size() + childElementSize;
796
bool ElementMap::empty() const
798
return mappedNames.empty() && childElementSize == 0;
801
IndexedName ElementMap::find(const MappedName& name, ElementIDRefs* sids) const
803
auto nameIter = mappedNames.find(name);
804
if (nameIter == mappedNames.end()) {
805
if (childElements.isEmpty()) {
806
return IndexedName();
810
if (name.findTagInElementName(nullptr, &len, nullptr, nullptr, false, false) < 0) {
811
return IndexedName();
813
QByteArray key = name.toRawBytes(len);
814
auto it = this->childElements.find(key);
815
if (it == this->childElements.end()) {
816
return IndexedName();
819
const auto& child = *it.value().childMap;
822
MappedName childName = MappedName::fromRawData(name, 0, len);
823
if (child.elementMap) {
824
res = child.elementMap->find(childName, sids);
827
res = childName.toIndexedName();
830
if (res && boost::equals(res.getType(), child.indexedName.getType())
831
&& child.indexedName.getIndex() <= res.getIndex()
832
&& child.indexedName.getIndex() + child.count > res.getIndex()) {
833
res.setIndex(res.getIndex() + it.value().childMap->offset);
837
return IndexedName();
841
const MappedNameRef* ref = findMappedRef(nameIter->second);
842
for (; ref; ref = ref->next.get()) {
843
if (ref->name == name) {
854
return nameIter->second;
857
MappedName ElementMap::find(const IndexedName& idx, ElementIDRefs* sids) const
863
auto iter = this->indexedNames.find(idx.getType());
864
if (iter == this->indexedNames.end()) {
868
auto& indices = iter->second;
869
if (idx.getIndex() < (int)indices.names.size()) {
870
const MappedNameRef& ref = indices.names[idx.getIndex()];
884
auto it = indices.children.upper_bound(idx.getIndex());
885
if (it != indices.children.end()
886
&& it->second.indexedName.getIndex() + it->second.offset <= idx.getIndex()) {
887
auto& child = it->second;
889
IndexedName childIdx(idx.getType(), idx.getIndex() - child.offset);
890
if (child.elementMap) {
891
name = child.elementMap->find(childIdx, sids);
894
name = MappedName(childIdx);
897
name += child.postfix;
904
std::vector<std::pair<MappedName, ElementIDRefs>> ElementMap::findAll(const IndexedName& idx) const
906
std::vector<std::pair<MappedName, ElementIDRefs>> res;
911
auto iter = this->indexedNames.find(idx.getType());
912
if (iter == this->indexedNames.end()) {
916
auto& indices = iter->second;
917
if (idx.getIndex() < (int)indices.names.size()) {
918
const MappedNameRef& ref = indices.names[idx.getIndex()];
920
for (auto nameRef = &ref; nameRef; nameRef = nameRef->next.get()) {
927
for (auto nameRef = &ref; nameRef; nameRef = nameRef->next.get()) {
929
res.emplace_back(nameRef->name, nameRef->sids);
936
auto it = indices.children.upper_bound(idx.getIndex());
937
if (it != indices.children.end()
938
&& it->second.indexedName.getIndex() + it->second.offset <= idx.getIndex()) {
939
auto& child = it->second;
940
IndexedName childIdx(idx.getType(), idx.getIndex() - child.offset);
941
if (child.elementMap) {
942
res = child.elementMap->findAll(childIdx);
943
for (auto& v : res) {
944
v.first += child.postfix;
948
res.emplace_back(MappedName(childIdx) + child.postfix, ElementIDRefs());
955
const MappedNameRef* ElementMap::findMappedRef(const IndexedName& idx) const
957
auto iter = this->indexedNames.find(idx.getType());
958
if (iter == this->indexedNames.end()) {
961
auto& indices = iter->second;
962
if (idx.getIndex() >= (int)indices.names.size()) {
965
return &indices.names[idx.getIndex()];
968
MappedNameRef* ElementMap::findMappedRef(const IndexedName& idx)
970
auto iter = this->indexedNames.find(idx.getType());
971
if (iter == this->indexedNames.end()) {
974
auto& indices = iter->second;
975
if (idx.getIndex() >= (int)indices.names.size()) {
978
return &indices.names[idx.getIndex()];
981
MappedNameRef& ElementMap::mappedRef(const IndexedName& idx)
984
auto& indices = this->indexedNames[idx.getType()];
985
if (idx.getIndex() >= (int)indices.names.size()) {
986
indices.names.resize(idx.getIndex() + 1);
988
return indices.names[idx.getIndex()];
991
bool ElementMap::hasChildElementMap() const
993
return !childElements.empty();
996
void ElementMap::hashChildMaps(long masterTag)
998
if (childElements.empty() || !this->hasher) {
1001
std::ostringstream ss;
1002
for (auto& indexedNameIndexedElements : this->indexedNames) {
1003
for (auto& indexedChild : indexedNameIndexedElements.second.children) {
1004
auto& child = indexedChild.second;
1007
int pos = MappedName::fromRawData(child.postfix)
1008
.findTagInElementName(&tag, &len, nullptr, nullptr, false, false);
1009
// TODO: What is this 10?
1011
MappedName postfix = hashElementName(
1012
MappedName::fromRawData(child.postfix.constData(), pos), child.sids);
1014
ss << MAPPED_CHILD_ELEMENTS_PREFIX << postfix;
1017
child.indexedName[0], tmp, ss, nullptr, masterTag, nullptr, child.tag, true);
1018
this->childElements.remove(child.postfix);
1019
child.postfix = tmp.toBytes();
1020
this->childElements[child.postfix].childMap = &child;
1026
void ElementMap::collectChildMaps(std::map<const ElementMap*, int>& childMapSet,
1027
std::vector<const ElementMap*>& childMaps,
1028
std::map<QByteArray, int>& postfixMap,
1029
std::vector<QByteArray>& postfixes) const
1031
auto res = childMapSet.insert(std::make_pair(this, 0));
1036
for (auto& indexedName : this->indexedNames) {
1037
addPostfix(QByteArray::fromRawData(indexedName.first,
1038
static_cast<int>(qstrlen(indexedName.first))),
1042
for (auto& childPair : indexedName.second.children) {
1043
auto& child = childPair.second;
1044
if (child.elementMap) {
1045
child.elementMap->collectChildMaps(childMapSet, childMaps, postfixMap, postfixes);
1050
for (auto& mappedName : this->mappedNames) {
1051
addPostfix(mappedName.first.constPostfix(), postfixMap, postfixes);
1054
childMaps.push_back(this);
1055
res.first->second = (int)childMaps.size();
1058
void ElementMap::addChildElements(long masterTag, const std::vector<MappedChildElements>& children)
1060
std::ostringstream ss;
1063
// To avoid possibly very long recursive child map lookup, resulting very
1064
// long mapped names, we try to resolve the grand child map now.
1065
std::vector<MappedChildElements> expansion;
1066
for (auto it = children.begin(); it != children.end(); ++it) {
1068
if (!child.elementMap || child.elementMap->childElements.empty()) {
1069
if (!expansion.empty()) {
1070
expansion.push_back(child);
1074
auto& indices = child.elementMap->indexedNames[child.indexedName.getType()];
1075
if (indices.children.empty()) {
1076
if (!expansion.empty()) {
1077
expansion.push_back(child);
1082
// Note that it is allowable to have both mapped names and child map. We
1083
// may have to split the current child mapping into pieces.
1085
int start = child.indexedName.getIndex();
1086
int end = start + child.count;
1087
for (auto iter = indices.children.upper_bound(start); iter != indices.children.end();
1089
auto& grandchild = iter->second;
1090
int istart = grandchild.indexedName.getIndex() + grandchild.offset;
1091
int iend = istart + grandchild.count;
1092
if (end <= istart) {
1095
if (istart >= end) {
1096
if (!expansion.empty()) {
1097
expansion.push_back(child);
1098
expansion.back().indexedName.setIndex(start);
1099
expansion.back().count = end - start;
1103
if (expansion.empty()) {
1104
const int extra {10};
1105
expansion.reserve(children.size() + extra);
1106
expansion.insert(expansion.end(), children.begin(), it);
1108
expansion.push_back(child);
1109
auto* entry = &expansion.back();
1110
if (istart > start) {
1111
entry->indexedName.setIndex(start);
1112
entry->count = istart - start;
1114
expansion.push_back(child);
1115
entry = &expansion.back();
1125
entry->indexedName.setIndex(istart - grandchild.offset);
1126
entry->count = iend - istart;
1127
entry->offset += grandchild.offset;
1128
entry->elementMap = grandchild.elementMap;
1129
entry->sids += grandchild.sids;
1130
if (grandchild.postfix.size() != 0) {
1131
if ((entry->postfix.size() != 0)
1132
&& !entry->postfix.startsWith(ELEMENT_MAP_PREFIX)) {
1133
entry->postfix = grandchild.postfix + ELEMENT_MAP_PREFIX + entry->postfix;
1136
entry->postfix = grandchild.postfix + entry->postfix;
1145
if (!expansion.empty() && start < end) {
1146
expansion.push_back(child);
1147
expansion.back().indexedName.setIndex(start);
1148
expansion.back().count = end - start;
1152
for (auto& child : expansion.empty() ? children : expansion) {
1153
if (!child.indexedName || (child.count == 0)) {
1154
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
1155
FC_ERR("invalid mapped child element");// NOLINT
1163
ChildMapInfo* entry = nullptr;
1165
// do child mapping only if the child element count >= 5
1166
const int threshold {5};
1167
if (child.count >= threshold || !child.elementMap) {
1168
encodeElementName(child.indexedName[0],
1173
child.postfix.constData(),
1177
// Perform some disambiguation in case the same shape is mapped
1178
// multiple times, e.g. draft array.
1179
entry = &childElements[tmp.toBytes()];
1180
int mapIndex = entry->mapIndices[child.elementMap.get()]++;
1182
if (entry->index != 1 && child.elementMap && mapIndex == 0) {
1183
// This child has duplicated 'tag' and 'postfix', but it
1184
// has its own element map. We'll expand this map now.
1190
IndexedName childIdx(child.indexedName);
1191
IndexedName idx(childIdx.getType(), childIdx.getIndex() + child.offset);
1192
for (int i = 0; i < child.count; ++i, ++childIdx, ++idx) {
1194
MappedName name = child.elementMap->find(childIdx, &sids);
1196
if ((child.tag == 0) || child.tag == masterTag) {
1197
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
1198
FC_WARN("unmapped element");// NOLINT
1202
name = MappedName(childIdx);
1206
idx[0], name, ss, &sids, masterTag, child.postfix.constData(), child.tag);
1207
setElementName(idx, name, masterTag, &sids);
1212
if (entry->index != 1) {
1213
// There is some ambiguity in child mapping. We need some
1214
// additional postfix for disambiguation. NOTE: We are not
1215
// using ComplexGeoData::indexPostfix() so we don't confuse
1216
// other code that actually uses this postfix for indexing
1217
// purposes. Here, we just need some postfix for
1218
// disambiguation. We don't need to extract the index.
1220
ss << ELEMENT_MAP_PREFIX << ":C" << entry->index - 1;
1223
encodeElementName(child.indexedName[0],
1228
child.postfix.constData(),
1232
entry = &childElements[tmp.toBytes()];
1233
if (entry->childMap) {
1234
FC_ERR("duplicate mapped child element");// NOLINT
1239
auto& indices = this->indexedNames[child.indexedName.getType()];
1240
auto res = indices.children.emplace(
1241
child.indexedName.getIndex() + child.offset + child.count, child);
1243
if (!entry->childMap) {
1244
this->childElements.remove(tmp.toBytes());
1246
FC_ERR("duplicate mapped child element");// NOLINT
1250
auto& insertedChild = res.first->second;
1251
insertedChild.postfix = tmp.toBytes();
1252
entry->childMap = &insertedChild;
1253
childElementSize += insertedChild.count;
1257
std::vector<ElementMap::MappedChildElements> ElementMap::getChildElements() const
1259
std::vector<MappedChildElements> res;
1260
res.reserve(this->childElements.size());
1261
for (auto& childElement : this->childElements) {
1262
res.push_back(*childElement.childMap);
1267
std::vector<MappedElement> ElementMap::getAll() const
1269
std::vector<MappedElement> ret;
1270
ret.reserve(size());
1271
for (auto& mappedName : this->mappedNames) {
1272
ret.emplace_back(mappedName.first, mappedName.second);
1274
for (auto& childElement : this->childElements) {
1275
auto& child = *childElement.childMap;
1276
IndexedName idx(child.indexedName);
1277
idx.setIndex(idx.getIndex() + child.offset);
1278
IndexedName childIdx(child.indexedName);
1279
for (int i = 0; i < child.count; ++i, ++idx, ++childIdx) {
1281
if (child.elementMap) {
1282
name = child.elementMap->find(childIdx);
1285
name = MappedName(childIdx);
1288
name += child.postfix;
1289
ret.emplace_back(name, idx);
1296
long ElementMap::getElementHistory(const MappedName& name, long masterTag, MappedName* original,
1297
std::vector<MappedName>* history) const
1301
int pos = name.findTagInElementName(&tag, &len, nullptr, nullptr, true);
1308
if (!original && !history) {
1313
MappedName& ret = original ? *original : tmp;
1314
if (name.startsWith(ELEMENT_MAP_PREFIX)) {
1315
unsigned offset = ELEMENT_MAP_PREFIX_SIZE;
1316
ret = MappedName::fromRawData(name, static_cast<int>(offset));
1323
if ((len == 0) || len > pos) {
1324
FC_WARN("invalid name length " << name);// NOLINT
1327
bool deHashed = false;
1328
if (ret.startsWith(MAPPED_CHILD_ELEMENTS_PREFIX, len)) {
1329
int offset = (int)POSTFIX_TAG_SIZE;
1330
MappedName tmp2 = MappedName::fromRawData(ret, len + offset, pos - len - offset);
1331
MappedName postfix = dehashElementName(tmp2);
1332
if (postfix != tmp2) {
1334
ret = MappedName::fromRawData(ret, 0, len) + postfix;
1338
ret = dehashElementName(MappedName::fromRawData(ret, 0, len));
1342
pos = ret.findTagInElementName(&tag2, &len, nullptr, nullptr, true);
1343
if (pos < 0 || (tag2 != tag && tag2 != -tag && tag != masterTag && -tag != masterTag)) {
1348
history->push_back(ret.copy());
1353
void ElementMap::traceElement(const MappedName& name, long masterTag, TraceCallback cb) const
1355
long encodedTag = 0;
1358
auto pos = name.findTagInElementName(&encodedTag, &len, nullptr, nullptr, true);
1359
if (cb(name, len, encodedTag, masterTag) || pos < 0) {
1363
if (name.startsWith(POSTFIX_EXTERNAL_TAG, len)) {
1367
std::set<long> tagSet;
1369
std::vector<MappedName> names;
1371
tagSet.insert(std::abs(masterTag));
1374
tagSet.insert(std::abs(encodedTag));
1376
names.push_back(name);
1378
masterTag = encodedTag;
1382
// TODO: element tracing without object is inherently unsafe, because of
1383
// possible external linking object which means the element may be encoded
1384
// using external string table. Looking up the wrong table may accidentally
1385
// cause circular mapping, and is actually quite easy to reproduce. See
1387
// https://github.com/realthunder/FreeCAD_assembly3/issues/968
1389
// An arbitrary depth limit is set here to not waste time. 'tagSet' above is
1390
// also used for early detection of 'recursive' mapping.
1392
for (int index = 0; index < 50; ++index) {
1393
if (!len || len > pos) {
1399
if (name.startsWith(ELEMENT_MAP_PREFIX)) {
1400
offset = ELEMENT_MAP_PREFIX_SIZE;
1402
tmp = MappedName(name, offset, len);
1405
tmp = MappedName(tmp, 0, len);
1407
tmp = dehashElementName(tmp);
1408
names.push_back(tmp);
1410
pos = tmp.findTagInElementName(&encodedTag, &len, nullptr, nullptr, true);
1411
if (pos >= 0 && tmp.startsWith(POSTFIX_EXTERNAL_TAG, len)) {
1415
if (encodedTag && masterTag != std::abs(encodedTag)
1416
&& !tagSet.insert(std::abs(encodedTag)).second) {
1417
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
1418
FC_WARN("circular element mapping");
1419
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) {
1420
auto doc = App::GetApplication().getActiveDocument();
1422
auto obj = doc->getObjectByID(masterTag);
1424
FC_LOG("\t" << obj->getFullName() << obj->getFullName() << "." << name);
1427
for (auto& errname : names) {
1428
FC_ERR("\t" << errname);
1435
if (cb(tmp, len, encodedTag, masterTag) || pos < 0) {
1438
masterTag = encodedTag;