App/Toponaming: Add tests for findTagInElementName
Also apply clang-tidy to that function, and fix an error with the postfix return.
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
|
||||
|
||||
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<boost::iostreams::array_source> iss(s, size);
|
||||
boost::iostreams::stream<boost::iostreams::array_source> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user