Address the poor performance of the existing unique-name generation (part 1) (#18589)
* Change Address the poor performance of the existing unique-name generation As described in Issue 16849, the existing Tools::getUniqueName method requires calling code to form a vector of existing names to be avoided. This leads to poor performance both in the O(n) cost of building such a vector and also getUniqueName's O(n) algorithm for actually generating the unique name (where 'n' is the number of pre-existing names). This has particularly noticeable cost in documents with large numbers of DocumentObjects because generating both Names and Labels for each new object incurs this cost. During an operation such as importing this results in an O(n^2) time spent generating names. The other major cost is in the saving of the temporary backup file, which uses name generation for the "files" embedded in the Zip file. Documents can easily need several such "files" for each object in the document. This is the first part of the correction, adding an efficient class for managing sets of unique names. New class UniqueNameManager keeps a list of existing names organized in a manner that eases unique-name generation. This class essentially acts as a set of names, with the ability to add and remove names and check if a name is already there, with the added ability to take a prototype name and generate a unique form for it which is not already in the set. a new unit test for UniqueNameManager has been added as well. There is a small regression, compared to the existing unique-name code, insofar as passing a prototype name like "xyz1234" to the old code would yield "xyz1235" whether or not "xyz1234" already existed, while the new code will return the next name above the currently-highest name on the "xyz" model, which could be "xyz" or "xyz1" or "xyz0042"..
This commit is contained in:
109
tests/src/Base/UniqueNameManager.cpp
Normal file
109
tests/src/Base/UniqueNameManager.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2024 Kevin Martin <kpmartin@papertrail.ca> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <Base/UniqueNameManager.h>
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-*,readability-*)
|
||||
TEST(UniqueNameManager, NonconflictingBaseNameReturnsSameName)
|
||||
{
|
||||
// Check that a model name that is a base name that does not conflict with
|
||||
// any existing name returns the base name
|
||||
EXPECT_EQ(Base::UniqueNameManager().makeUniqueName("Body"), "Body");
|
||||
}
|
||||
|
||||
TEST(UniqueNameManager, NonconflictingUniqueNameReturnsBaseName)
|
||||
{
|
||||
// Check that a model name that is a unique name whose base name does not conflict with
|
||||
// any existing name returns the base name
|
||||
EXPECT_EQ(Base::UniqueNameManager().makeUniqueName("Body123"), "Body");
|
||||
}
|
||||
|
||||
TEST(UniqueNameManager, ManyDigitsInModelNameIgnored)
|
||||
{
|
||||
// Check that the number of digits in the model name does not affect the number
|
||||
// of digits in the returned name.
|
||||
Base::UniqueNameManager manager;
|
||||
manager.addExactName("Body");
|
||||
EXPECT_EQ(manager.makeUniqueName("Body12345", 3), "Body001");
|
||||
}
|
||||
|
||||
TEST(UniqueNameManager, ConflictingBaseNameReturnsUniqueName)
|
||||
{
|
||||
// Check that a model name that conflicts with an existing name returns a unique form of
|
||||
// the base name
|
||||
Base::UniqueNameManager manager;
|
||||
manager.addExactName("Body");
|
||||
EXPECT_EQ(manager.makeUniqueName("Body", 1), "Body1");
|
||||
}
|
||||
|
||||
TEST(UniqueNameManager, ConflictingUniqueNameReturnsNewUniqueName)
|
||||
{
|
||||
// Check that a given unique name already in the collection causes the return of
|
||||
// a new unique name.
|
||||
Base::UniqueNameManager manager;
|
||||
manager.addExactName("Body");
|
||||
manager.addExactName("Body001");
|
||||
EXPECT_EQ(manager.makeUniqueName("Body001", 3), "Body002");
|
||||
}
|
||||
|
||||
TEST(UniqueNameManager, VerifyUniqueDigitCount)
|
||||
{
|
||||
// Check that when a unique name is generated it contains at least the specified number
|
||||
// of inserted digits
|
||||
Base::UniqueNameManager manager;
|
||||
manager.addExactName("Body");
|
||||
EXPECT_EQ(manager.makeUniqueName("Body", 3), "Body001");
|
||||
}
|
||||
|
||||
TEST(UniqueNameManager, UniqueNameLargerThanAnyName)
|
||||
{
|
||||
// Check that the name returned is larger (using digits) that any wxisting name
|
||||
// even if the given model name is not itself in the collection
|
||||
Base::UniqueNameManager manager;
|
||||
manager.addExactName("Body001");
|
||||
EXPECT_EQ(manager.makeUniqueName("Body", 3), "Body002");
|
||||
}
|
||||
|
||||
TEST(UniqueNameManager, Issue18504)
|
||||
{
|
||||
// Check that the management of spans of assigned unique digit values is
|
||||
// correct
|
||||
std::string name;
|
||||
Base::UniqueNameManager manager;
|
||||
manager.addExactName("Origin007");
|
||||
manager.addExactName("Origin");
|
||||
manager.addExactName("Origin008");
|
||||
manager.addExactName("Origin010");
|
||||
manager.addExactName("Origin011");
|
||||
manager.addExactName("Origin013");
|
||||
manager.addExactName("Origin016");
|
||||
|
||||
name = manager.makeUniqueName("Origin", 3);
|
||||
manager.addExactName(name);
|
||||
|
||||
name = manager.makeUniqueName("Origin", 3);
|
||||
manager.addExactName(name);
|
||||
|
||||
name = manager.makeUniqueName("Origin", 3);
|
||||
EXPECT_NE(name, "Origin010");
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-*,readability-*)
|
||||
Reference in New Issue
Block a user