diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index 0f62e3fc93..9e39630752 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -577,6 +577,7 @@ libtint_source_set("libtint_core_all_src") { "utils/map.h", "utils/math.h", "utils/scoped_assignment.h", + "utils/string.cc", "utils/string.h", "utils/unique_allocator.h", "utils/unique_vector.h", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 940c96b3db..c331ccfa8d 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -486,6 +486,7 @@ set(TINT_LIB_SRCS utils/map.h utils/math.h utils/scoped_assignment.h + utils/string.cc utils/string.h utils/unique_allocator.h utils/unique_vector.h diff --git a/src/tint/utils/string.cc b/src/tint/utils/string.cc new file mode 100644 index 0000000000..a0d3b855b5 --- /dev/null +++ b/src/tint/utils/string.cc @@ -0,0 +1,51 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "src/tint/utils/string.h" +#include "src/tint/utils/vector.h" + +namespace tint::utils { + +size_t Distance(const std::string& str_a, const std::string& str_b) { + const auto len_a = str_a.size(); + const auto len_b = str_b.size(); + + Vector mat; + mat.Reserve((len_a + 1) * (len_b + 1)); + + auto at = [&](size_t a, size_t b) -> size_t& { return mat[a + b * (len_a + 1)]; }; + + at(0, 0) = 0; + for (size_t a = 1; a <= len_a; a++) { + at(a, 0) = a; + } + for (size_t b = 1; b <= len_b; b++) { + at(0, b) = b; + } + for (size_t b = 1; b <= len_b; b++) { + for (size_t a = 1; a <= len_a; a++) { + bool eq = str_a[a - 1] == str_b[b - 1]; + at(a, b) = std::min({ + at(a - 1, b) + 1, + at(a, b - 1) + 1, + at(a - 1, b - 1) + (eq ? 0 : 1), + }); + } + } + return at(len_a, len_b); +} + +} // namespace tint::utils diff --git a/src/tint/utils/string.h b/src/tint/utils/string.h index de1f4d6abb..9f65395595 100644 --- a/src/tint/utils/string.h +++ b/src/tint/utils/string.h @@ -44,6 +44,11 @@ std::string ToString(const T& value) { return s.str(); } +/// @param a the first string +/// @param b the second string +/// @returns the Levenshtein distance between @p a and @p b +size_t Distance(const std::string& a, const std::string& b); + } // namespace tint::utils #endif // SRC_TINT_UTILS_STRING_H_ diff --git a/src/tint/utils/string_test.cc b/src/tint/utils/string_test.cc index d1cab51e95..6c8008cb2c 100644 --- a/src/tint/utils/string_test.cc +++ b/src/tint/utils/string_test.cc @@ -20,21 +20,33 @@ namespace tint::utils { namespace { TEST(StringTest, ReplaceAll) { - ASSERT_EQ("xybbcc", ReplaceAll("aabbcc", "aa", "xy")); - ASSERT_EQ("aaxycc", ReplaceAll("aabbcc", "bb", "xy")); - ASSERT_EQ("aabbxy", ReplaceAll("aabbcc", "cc", "xy")); - ASSERT_EQ("xyxybbcc", ReplaceAll("aabbcc", "a", "xy")); - ASSERT_EQ("aaxyxycc", ReplaceAll("aabbcc", "b", "xy")); - ASSERT_EQ("aabbxyxy", ReplaceAll("aabbcc", "c", "xy")); + EXPECT_EQ("xybbcc", ReplaceAll("aabbcc", "aa", "xy")); + EXPECT_EQ("aaxycc", ReplaceAll("aabbcc", "bb", "xy")); + EXPECT_EQ("aabbxy", ReplaceAll("aabbcc", "cc", "xy")); + EXPECT_EQ("xyxybbcc", ReplaceAll("aabbcc", "a", "xy")); + EXPECT_EQ("aaxyxycc", ReplaceAll("aabbcc", "b", "xy")); + EXPECT_EQ("aabbxyxy", ReplaceAll("aabbcc", "c", "xy")); // Replacement string includes the searched-for string. // This proves that the algorithm needs to advance 'pos' // past the replacement. - ASSERT_EQ("aabxybbxybcc", ReplaceAll("aabbcc", "b", "bxyb")); + EXPECT_EQ("aabxybbxybcc", ReplaceAll("aabbcc", "b", "bxyb")); } TEST(StringTest, ToString) { - ASSERT_EQ("123", ToString(123)); - ASSERT_EQ("hello", ToString("hello")); + EXPECT_EQ("123", ToString(123)); + EXPECT_EQ("hello", ToString("hello")); +} + +TEST(StringTest, Distance) { + EXPECT_EQ(Distance("hello world", "hello world"), 0u); + EXPECT_EQ(Distance("hello world", "helloworld"), 1u); + EXPECT_EQ(Distance("helloworld", "hello world"), 1u); + EXPECT_EQ(Distance("hello world", "hello world"), 1u); + EXPECT_EQ(Distance("hello world", "hello world"), 1u); + EXPECT_EQ(Distance("Hello World", "hello world"), 2u); + EXPECT_EQ(Distance("hello world", "Hello World"), 2u); + EXPECT_EQ(Distance("Hello world", ""), 11u); + EXPECT_EQ(Distance("", "Hello world"), 11u); } } // namespace