diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 24ea1835a..ea4b1209c 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -11,6 +11,8 @@ if (GTEST_FOUND) mwdialogue/test_keywordsearch.cpp esm/test_fixed_string.cpp + + misc/test_stringops.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/misc/test_stringops.cpp b/apps/openmw_test_suite/misc/test_stringops.cpp new file mode 100644 index 000000000..c47e49724 --- /dev/null +++ b/apps/openmw_test_suite/misc/test_stringops.cpp @@ -0,0 +1,44 @@ +#include +#include "components/misc/stringops.hpp" + +struct PartialBinarySearchTest : public ::testing::Test +{ + protected: + std::vector mDataVec; + virtual void SetUp() + { + const char* data[] = { "Head", "Chest", "Tri Head", "Tri Chest", "Bip01" }; + mDataVec = std::vector(data, data+sizeof(data)/sizeof(data[0])); + std::sort(mDataVec.begin(), mDataVec.end(), Misc::StringUtils::ciLess); + } + + virtual void TearDown() + { + } + + bool matches(const std::string& keyword) + { + return Misc::StringUtils::partialBinarySearch(mDataVec.begin(), mDataVec.end(), keyword) != mDataVec.end(); + } +}; + +TEST_F(PartialBinarySearchTest, partial_binary_search_test) +{ + EXPECT_TRUE( matches("Head 01") ); + EXPECT_TRUE( matches("Head") ); + EXPECT_TRUE( matches("Tri Head 01") ); + EXPECT_TRUE( matches("Tri Head") ); + EXPECT_TRUE( matches("tri head") ); + + EXPECT_FALSE( matches(" Head") ); + EXPECT_FALSE( matches("Tri Head") ); +} + +TEST_F (PartialBinarySearchTest, ci_test) +{ + EXPECT_TRUE (Misc::StringUtils::lowerCase("ASD") == "asd"); + + // test to make sure system locale is not used + std::string unicode1 = "\u04151 \u0418"; // CYRILLIC CAPITAL LETTER IE CYRILLIC CAPITAL LETTER I + EXPECT_TRUE( Misc::StringUtils::lowerCase(unicode1) == unicode1 ); +} diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index b14051a81..9acd81710 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -114,6 +114,30 @@ public: return ciLess(left, right); } }; + + + /// Performs a binary search on a sorted container for a string that 'key' starts with + template + static Iterator partialBinarySearch(Iterator begin, Iterator end, const T& key) + { + const Iterator notFound = end; + + while(begin < end) + { + const Iterator middle = begin + (std::distance(begin, end) / 2); + + int comp = Misc::StringUtils::ciCompareLen((*middle), key, (*middle).size()); + + if(comp == 0) + return middle; + else if(comp > 0) + end = middle; + else + begin = middle + 1; + } + + return notFound; + } }; } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 167ab6221..ba8c57c1b 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -383,18 +383,25 @@ namespace Resource public: bool isReservedName(const std::string& name) const { - static std::set reservedNames; + if (name.empty()) + return false; + + static std::vector reservedNames; if (reservedNames.empty()) { const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm", "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle", "Left Clavicle", "Weapon Bone", "Tail", "Bip01 L Hand", "Bip01 R Hand", "Bip01 Head", "Bip01 Spine1", "Bip01 Spine2", "Bip01 L Clavicle", "Bip01 R Clavicle", "bip01", "Root Bone", "Bip01 Neck", "BoneOffset", "AttachLight", "ArrowBone", "Camera"}; - reservedNames = std::set(reserved, reserved + sizeof(reserved)/sizeof(reserved[0])); + reservedNames = std::vector(reserved, reserved + sizeof(reserved)/sizeof(reserved[0])); for (unsigned int i=0; i::iterator it = Misc::StringUtils::partialBinarySearch(reservedNames.begin(), reservedNames.end(), name); + return it != reservedNames.end(); } virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const