diff --git a/src/App/MappedName.cpp b/src/App/MappedName.cpp index abf8b39721..17256b32e4 100644 --- a/src/App/MappedName.cpp +++ b/src/App/MappedName.cpp @@ -35,7 +35,7 @@ #include -FC_LOG_LEVEL_INIT("MappedName", true, 2); +FC_LOG_LEVEL_INIT("MappedName", true, 2);// NOLINT namespace Data { @@ -59,13 +59,13 @@ void MappedName::compact() const } -int MappedName::findTagInElementName(long* tag, int* len, const char* postfix, - char* type, bool negative, bool recursive) const +int MappedName::findTagInElementName(long* tagOut, int* lenOut, std::string* postfixOut, + char* typeOut, bool negative, bool recursive) const { bool hex = true; int pos = this->rfind(POSTFIX_TAG); - // Example name, tagPosfix == ;:H + // Example name, POSTFIX_TAG == ;:H // #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F // ^ // | @@ -73,8 +73,9 @@ int MappedName::findTagInElementName(long* tag, int* len, const char* postfix, if(pos < 0) { pos = this->rfind(POSTFIX_DECIMAL_TAG); - if (pos < 0) + if (pos < 0) { return -1; + } hex = false; } int offset = pos + (int)POSTFIX_TAG_SIZE; @@ -85,78 +86,84 @@ int MappedName::findTagInElementName(long* tag, int* len, const char* postfix, char tp = 0; char eof = 0; - int size; - const char *s = this->toConstString(offset, size); + int size {0}; + const char * nameAsChars = this->toConstString(offset, size); // check if the number followed by the tagPosfix is negative - bool isNegative = (s[0] == '-'); + bool isNegative = (nameAsChars[0] == '-'); if (isNegative) { - ++s; + ++nameAsChars; --size; } - boost::iostreams::stream iss(s, size); + boost::iostreams::stream iss(nameAsChars, size); if (!hex) { // no hex is an older version of the encoding scheme iss >> _tag >> sep; } else { - // The purpose of tag postfix is to encode one model operation. The - // 'tag' field is used to record the own object ID of that model shape, - // and the 'len' field indicates the length of the operation codes - // before the tag postfix. These fields are in hex. The trailing 'F' is - // the shape type of this element, 'F' for face, 'E' edge, and 'V' vertex. + // The purpose of tagOut postfixOut is to encode one model operation. The + // 'tagOut' field is used to record the own object ID of that model shape, + // and the 'lenOut' field indicates the length of the operation codes + // before the tagOut postfixOut. These fields are in hex. The trailing 'F' is + // the shape typeOut of this element, 'F' for face, 'E' edge, and 'V' vertex. // // #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F // | | ^^ ^^ // | | | | - // ---len = 0x10--- tag len + // ---lenOut = 0x10--- tagOut lenOut iss >> std::hex; // _tag field can be skipped, if it is 0 - if (s[0] == ',' || s[0] == ':') + if (nameAsChars[0] == ',' || nameAsChars[0] == ':') { iss >> sep; - else + } + else { iss >> _tag >> sep; + } } - if (isNegative) + if (isNegative) { _tag = -_tag; + } if (sep == ':') { // ':' is followed by _len field. // // For decTagPostfix() (i.e. older encoding scheme), this is the length - // of the string before the entire postfix (A postfix may contain + // of the string before the entire postfixOut (A postfixOut may contain // multiple segments usually separated by ELEMENT_MAP_PREFIX. // // For newer POSTFIX_TAG, this counts the number of characters that - // proceeds this tag postfix segment that forms the op code (see + // proceeds this tagOut postfixOut segment that forms the op code (see // example above). // - // The reason of this change is so that the postfix can stay the same + // The reason of this change is so that the postfixOut can stay the same // regardless of the prefix, which can increase memory efficiency. // iss >> _len >> sep2 >> tp >> eof; - // The next separator to look for is either ':' for older tag postfix, or ',' - if (!hex && sep2 == ':') + // The next separator to look for is either ':' for older tagOut postfixOut, or ',' + if (!hex && sep2 == ':') { sep2 = ','; + } } else if (hex && sep == ',') { - // ',' is followed by a single character that indicates the element type. + // ',' is followed by a single character that indicates the element typeOut. iss >> tp >> eof; sep = ':'; sep2 = ','; } - if (_len < 0 || sep != ':' || sep2 != ',' || tp == 0 || eof != 0) + if (_len < 0 || sep != ':' || sep2 != ',' || tp == 0 || eof != 0) { return -1; + } if (hex) { - if (pos-_len < 0) - return -1; - if (_len && recursive && (tag || len)) { - // in case of recursive tag postfix (used by hierarchy element - // map), look for any embedded tag postfix + if (pos-_len < 0) { + return -1; + } + if ((_len != 0) && recursive && (tagOut || lenOut)) { + // in case of recursive tagOut postfixOut (used by hierarchy element + // map), look for any embedded tagOut postfixOut int next = MappedName::fromRawData(*this, pos-_len, _len).rfind(POSTFIX_TAG); if (next >= 0) { next += pos - _len; @@ -165,14 +172,17 @@ int MappedName::findTagInElementName(long* tag, int* len, const char* postfix, // | | // next pos // - // There maybe other operation codes after this embedded tag - // postfix, search for the sperator. + // There maybe other operation codes after this embedded tagOut + // postfixOut, search for the seperator. // - int end; - if (pos == next) + int end {0}; + if (pos == next) { end = -1; - else - end = MappedName::fromRawData(*this, next+1, pos-next-1).find(ELEMENT_MAP_PREFIX); + } + else { + end = MappedName::fromRawData(*this, next + 1, pos - next - 1) + .find(ELEMENT_MAP_PREFIX); + } if (end >= 0) { end += next+1; // #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F @@ -182,33 +192,41 @@ int MappedName::findTagInElementName(long* tag, int* len, const char* postfix, _len = pos - end; // #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F // | | - // -- len -- - } else + // -- lenOut -- + } else { _len = 0; + } } } - // Now convert the 'len' field back to the length of the remaining name + // Now convert the 'lenOut' field back to the length of the remaining name // // #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F // | | - // ----------- len ----------- + // ----------- lenOut ----------- _len = pos - _len; } - if(type) - *type = tp; - if(tag) { - if (_tag == 0 && recursive) - return MappedName(*this, 0, _len).findTagInElementName(tag, len, postfix, type, negative); - if(_tag>0 || negative) - *tag = _tag; - else - *tag = -_tag; + if(typeOut) { + *typeOut = tp; + } + if(tagOut) { + if (_tag == 0 && recursive) { + return MappedName(*this, 0, _len) + .findTagInElementName(tagOut, lenOut, postfixOut, typeOut, negative); + } + if(_tag>0 || negative) { + *tagOut = _tag; + } + else { + *tagOut = -_tag; + } + } + if(lenOut) { + *lenOut = _len; + } + if(postfixOut) { + *postfixOut = this->toString(pos); } - if(len) - *len = _len; - if(postfix) - this->toString(*postfix, pos); return pos; } diff --git a/src/App/MappedName.h b/src/App/MappedName.h index 8e3ef863db..355533e1bd 100644 --- a/src/App/MappedName.h +++ b/src/App/MappedName.h @@ -896,21 +896,21 @@ public: offset); } - /// Extract tag and other information from a encoded element name + /// Extract tagOut and other information from a encoded element name /// - /// \param tag: optional pointer to receive the extracted tag - /// \param len: optional pointer to receive the length field after the tag field. + /// \param tagOut: optional pointer to receive the extracted tagOut + /// \param lenOut: optional pointer to receive the length field after the tagOut field. /// This gives the length of the previous hashed element name starting /// from the beginning of the give element name. - /// \param postfix: optional pointer to receive the postfix starting at the found tag field. - /// \param type: optional pointer to receive the element type character - /// \param negative: return negative tag as it is. If disabled, then always return positive tag. - /// Negative tag is sometimes used for element disambiguation. - /// \param recursive: recursively find the last non-zero tag + /// \param postfixOut: optional pointer to receive the postfixOut starting at the found tagOut field. + /// \param typeOut: optional pointer to receive the element typeOut character + /// \param negative: return negative tagOut as it is. If disabled, then always return positive tagOut. + /// Negative tagOut is sometimes used for element disambiguation. + /// \param recursive: recursively find the last non-zero tagOut /// - /// \return Return the end position of the tag field, or return -1 if not found. - int findTagInElementName(long* tag = nullptr, int* len = nullptr, const char* postfix = nullptr, - char* type = nullptr, bool negative = false, + /// \return Return the end position of the tagOut field, or return -1 if not found. + int findTagInElementName(long* tagOut = nullptr, int* lenOut = nullptr, std::string* postfixOut = nullptr, + char* typeOut = nullptr, bool negative = false, bool recursive = true) const; /// Get a hash for this MappedName diff --git a/tests/src/App/MappedName.cpp b/tests/src/App/MappedName.cpp index 77b6b297f0..35bf068ad7 100644 --- a/tests/src/App/MappedName.cpp +++ b/tests/src/App/MappedName.cpp @@ -833,6 +833,125 @@ TEST(MappedName, startsWith) EXPECT_EQ(mappedName.startsWith("WASD"), false); } +TEST(MappedName, findTagInElementNameHexPositiveIndexNonrecursive) +{ + // The non-recursive version will find just the last tag, prefixed by the POSTFIX_TAG (";:H"). + // It consists of a tag (stored in hexadecimal) and a length (also stored in hex), separated by + // a colon. In this example, we expect the get ;:H1b:10,F as our element, which is a tag of + // 0x1b and an indicated length of 0x10 (16 bytes). So the output length is the position of the + // tag (36) minus the indicated length, giving 20. + + // Arrange + Data::MappedName mappedName("#94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F"); + long tagOutput {0}; + int lenOutput {0}; + std::string postfix; + char type {0}; + + // Act + int result = + mappedName.findTagInElementName(&tagOutput, &lenOutput, &postfix, &type, false, false); + + // Assert + EXPECT_EQ(result, 36); // The location of the tag + EXPECT_EQ(tagOutput, 0x1b);// The tag + EXPECT_EQ(lenOutput, 20); // The calculated length based on the tag's length parameter + EXPECT_EQ(postfix, ";:H1b:10,F"); + EXPECT_EQ(type, 'F');// F=Face +} + +TEST(MappedName, findTagInElementNameDecPositiveIndexNonrecursive) +{ + // Test backwards compatibility with the older style decimal tag storage. + + // Arrange + Data::MappedName mappedName("#94;:G0;XTR;:T19:8,F;:T26,F;BND:-1:0;:T27:16,F"); + long tagOutput {0}; + int lenOutput {0}; + std::string postfix; + char type {0}; + + // Act + int result = + mappedName.findTagInElementName(&tagOutput, &lenOutput, &postfix, &type, false, false); + + // Assert + EXPECT_EQ(result, 36); // The location of the tag + EXPECT_EQ(tagOutput, 27);// The tag + EXPECT_EQ(lenOutput, 16);// The specified length + EXPECT_EQ(postfix, ";:T27:16,F"); + EXPECT_EQ(type, 'F');// F=Face +} + +TEST(MappedName, findTagInElementNameHexNegativeIndexNonrecursive) +{ + // Test handling a negative index that is flipped to positive + + // Arrange + Data::MappedName mappedName("#94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H-1b:10,F"); + long tagOutput {0}; + int lenOutput {0}; + std::string postfix; + char type {0}; + + // Act + int result = + mappedName.findTagInElementName(&tagOutput, &lenOutput, &postfix, &type, false, false); + + // Assert + EXPECT_EQ(result, 36); // The location of the tag + EXPECT_EQ(tagOutput, 0x1b);// The tag is returned positive, even though it was input negative + EXPECT_EQ(lenOutput, 20); // The calculated length based on the tag's length parameter + EXPECT_EQ(postfix, ";:H-1b:10,F"); + EXPECT_EQ(type, 'F');// F=Face +} + +TEST(MappedName, findTagInElementNameHexExpectedNegativeIndexNonrecursive) +{ + // Test handling an untransformed negative index + + // Arrange + Data::MappedName mappedName("#94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H-1b:10,F"); + long tagOutput {0}; + int lenOutput {0}; + std::string postfix; + char type {0}; + + // Act + int result = + mappedName.findTagInElementName(&tagOutput, &lenOutput, &postfix, &type, true, false); + + // Assert + EXPECT_EQ(result, 36); // The location of the tag + EXPECT_EQ(tagOutput, -0x1b);// The tag is returned negative + EXPECT_EQ(lenOutput, 20); // The calculated length based on the tag's length parameter + EXPECT_EQ(postfix, ";:H-1b:10,F"); + EXPECT_EQ(type, 'F');// F=Face +} + +TEST(MappedName, findTagInElementNameRecursive) +{ + // Test the recursive resolution of the name + + // Arrange + Data::MappedName mappedName("#94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F"); + long tagOutput {0}; + int lenOutput {0}; + std::string postfix; + char type {0}; + + // Act + int result = + mappedName.findTagInElementName(&tagOutput, &lenOutput, &postfix, &type, false, true); + + // Assert + EXPECT_EQ(result, 36); // The location of the tag + EXPECT_EQ(tagOutput, 0x1b);// The tag + EXPECT_EQ(lenOutput, 27); // Now includes the next tag, from the recursive search + EXPECT_EQ(postfix, ";:H1b:10,F"); + EXPECT_EQ(type, 'F');// F=Face +} + TEST(MappedName, hash) { // Arrange