This is an automated email from the ASF dual-hosted git repository.

junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new bd12b26  feat: add find and substr methods to String (#383)
bd12b26 is described below

commit bd12b26ac36ae6e770d128710fba13108957ee52
Author: Guan-Ming (Wesley) Chiu <[email protected]>
AuthorDate: Wed Jan 7 00:50:35 2026 +0800

    feat: add find and substr methods to String (#383)
    
    ## Why
    
    String class lacks essential string manipulation methods that are
    commonly expected in standard library equivalents, requiring users to
    convert to std::string for basic operations like substring search and
    extraction.
    
    ## How
    
    - Add find() method with three overloads (String, const char*, const
    char* with count) to search for substrings
    - Add substr() method to extract substrings with bounds checking
---
 include/tvm/ffi/string.h    | 44 ++++++++++++++++++++++++++++++++++++++++++++
 tests/cpp/test_string.cc    | 37 +++++++++++++++++++++++++++++++++++++
 tests/python/test_string.py | 18 ++++++++++++++++++
 3 files changed, 99 insertions(+)

diff --git a/include/tvm/ffi/string.h b/include/tvm/ffi/string.h
index b18e690..b93c3a9 100644
--- a/include/tvm/ffi/string.h
+++ b/include/tvm/ffi/string.h
@@ -611,6 +611,50 @@ class String {
     }
   }
 
+  /*! \brief Value returned by find() when no match is found */
+  static constexpr size_t npos = static_cast<size_t>(-1);
+
+  /*!
+   * \brief Find the first occurrence of a substring
+   * \param str The substring to search for
+   * \param pos The position at which to start the search
+   * \return The position of the first character of the first match, or npos 
if not found
+   */
+  size_t find(const String& str, size_t pos = 0) const { return 
find(str.data(), pos, str.size()); }
+
+  /*!
+   * \brief Find the first occurrence of a substring
+   * \param str The substring to search for
+   * \param pos The position at which to start the search
+   * \return The position of the first character of the first match, or npos 
if not found
+   */
+  size_t find(const char* str, size_t pos = 0) const { return find(str, pos, 
std::strlen(str)); }
+
+  /*!
+   * \brief Find the first occurrence of a substring
+   * \param str The substring to search for
+   * \param pos The position at which to start the search
+   * \param count The length of the substring
+   * \return The position of the first character of the first match, or npos 
if not found
+   */
+  size_t find(const char* str, size_t pos, size_t count) const {
+    return std::string_view(data(), size()).find(std::string_view(str, count), 
pos);
+  }
+
+  /*!
+   * \brief Returns a substring [pos, pos+count)
+   * \param pos The position of the first character to include
+   * \param count The length of the substring (default: until end of string)
+   * \return A string containing the substring
+   */
+  String substr(size_t pos = 0, size_t count = size_t(-1)) const {
+    if (pos > size()) {
+      throw std::out_of_range("tvm::String substr index out of bounds");
+    }
+    size_t rcount = std::min(count, size() - pos);
+    return String(data() + pos, rcount);
+  }
+
   /*!
    * \brief Convert String to an std::string object
    *
diff --git a/tests/cpp/test_string.cc b/tests/cpp/test_string.cc
index 072c5cd..afcb651 100644
--- a/tests/cpp/test_string.cc
+++ b/tests/cpp/test_string.cc
@@ -445,4 +445,41 @@ TEST(String, StdHash) {
   EXPECT_EQ(std::hash<Bytes>()(s3), std::hash<Bytes>()(s4));
 }
 
+TEST(String, Find) {
+  String s{"hello world"};
+  EXPECT_EQ(s.find("world"), 6);
+  EXPECT_EQ(s.find("hello"), 0);
+  EXPECT_EQ(s.find("o"), 4);
+  EXPECT_EQ(s.find("o", 5), 7);
+  EXPECT_EQ(s.find("notfound"), String::npos);
+  EXPECT_EQ(s.find(""), 0);
+  EXPECT_EQ(s.find("", 5), 5);
+  EXPECT_EQ(s.find("", 11), 11);
+  EXPECT_EQ(s.find("", 20), String::npos);
+
+  String pattern{"world"};
+  EXPECT_EQ(s.find(pattern), 6);
+
+  String empty{""};
+  EXPECT_EQ(empty.find("x"), String::npos);
+  EXPECT_EQ(empty.find(""), 0);
+}
+
+TEST(String, Substr) {
+  String s{"hello world"};
+  EXPECT_EQ(s.substr(0, 5), "hello");
+  EXPECT_EQ(s.substr(6, 5), "world");
+  EXPECT_EQ(s.substr(6), "world");
+  EXPECT_EQ(s.substr(0), "hello world");
+  EXPECT_EQ(s.substr(11), "");
+  EXPECT_EQ(s.substr(0, 0), "");
+
+  EXPECT_THROW(s.substr(12), std::out_of_range);
+  EXPECT_THROW(s.substr(100), std::out_of_range);
+
+  String empty{""};
+  EXPECT_EQ(empty.substr(0), "");
+  EXPECT_THROW(empty.substr(1), std::out_of_range);
+}
+
 }  // namespace
diff --git a/tests/python/test_string.py b/tests/python/test_string.py
index 5dd06f4..499270b 100644
--- a/tests/python/test_string.py
+++ b/tests/python/test_string.py
@@ -53,3 +53,21 @@ def test_bytes() -> None:
     b5 = pickle.loads(pickle.dumps(b))
     assert b5 == b"hello"
     assert isinstance(b5, tvm_ffi.core.Bytes)
+
+
+def test_string_find_substr() -> None:
+    s = tvm_ffi.core.String("hello world")
+    assert s.find("world") == 6
+    assert s.find("hello") == 0
+    assert s.find("o") == 4
+    assert s.find("o", 5) == 7
+    assert s.find("notfound") == -1
+    assert s.find("") == 0
+    assert s.find("", 5) == 5
+    assert s.find("", 11) == 11
+    assert s.find("", 20) == -1
+
+    assert s[6:11] == "world"
+    assert s[0:5] == "hello"
+    assert s[6:] == "world"
+    assert s[:5] == "hello"

Reply via email to