]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Add Tokenizer class
authorJoel Rosdahl <joel@rosdahl.net>
Wed, 15 Apr 2020 19:33:58 +0000 (21:33 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Sun, 27 Jun 2021 07:01:17 +0000 (09:01 +0200)
The class behaves like a collection and can be used in a range-based for
loop.

src/CMakeLists.txt
src/Util.cpp
src/util/CMakeLists.txt [new file with mode: 0644]
src/util/Tokenizer.cpp [new file with mode: 0644]
src/util/Tokenizer.hpp [new file with mode: 0644]
unittest/CMakeLists.txt
unittest/test_Util.cpp
unittest/test_util_Tokenizer.cpp [new file with mode: 0644]

index beefd81f1fc2a1efc36bc581a53c5ba8278ec6d1..0a6e8bfe9a5dadb4792f8f1469391b73c54567cc 100644 (file)
@@ -80,3 +80,4 @@ target_link_libraries(
 target_include_directories(ccache_lib PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
 
 add_subdirectory(third_party)
+add_subdirectory(util)
index ed6d78911286c216cff64cac8ef62c5995ce8072..a60fa791745935df3ae076dc34c4a313de44f622 100644 (file)
@@ -26,6 +26,8 @@
 #include "TemporaryFile.hpp"
 #include "fmtmacros.hpp"
 
+#include <util/Tokenizer.hpp>
+
 extern "C" {
 #include "third_party/base32hex.h"
 }
@@ -136,26 +138,12 @@ path_max(const std::string& path)
 
 template<typename T>
 std::vector<T>
-split_at(string_view input, const char* separators)
+split_into(string_view input, const char* separators)
 {
-  ASSERT(separators != nullptr && separators[0] != '\0');
-
   std::vector<T> result;
-
-  size_t start = 0;
-  while (start < input.size()) {
-    size_t end = input.find_first_of(separators, start);
-
-    if (end == string_view::npos) {
-      result.emplace_back(input.data() + start, input.size() - start);
-      break;
-    } else if (start != end) {
-      result.emplace_back(input.data() + start, end - start);
-    }
-
-    start = end + 1;
+  for (const auto token : util::Tokenizer(input, separators)) {
+    result.emplace_back(token);
   }
-
   return result;
 }
 
@@ -1357,13 +1345,13 @@ setenv(const std::string& name, const std::string& value)
 std::vector<string_view>
 split_into_views(string_view input, const char* separators)
 {
-  return split_at<string_view>(input, separators);
+  return split_into<string_view>(input, separators);
 }
 
 std::vector<std::string>
 split_into_strings(string_view input, const char* separators)
 {
-  return split_at<std::string>(input, separators);
+  return split_into<std::string>(input, separators);
 }
 
 std::string
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3401cd6
--- /dev/null
@@ -0,0 +1,6 @@
+set(
+  sources
+  ${CMAKE_CURRENT_SOURCE_DIR}/Tokenizer.cpp
+)
+
+target_sources(ccache_lib PRIVATE ${sources})
diff --git a/src/util/Tokenizer.cpp b/src/util/Tokenizer.cpp
new file mode 100644 (file)
index 0000000..c201404
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program 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 General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "Tokenizer.hpp"
+
+namespace util {
+
+Tokenizer::Iterator
+Tokenizer::Iterator::operator++()
+{
+  if (m_pos >= m_string.size()) {
+    return *this;
+  }
+
+  m_pos = m_string.find_first_not_of(m_delimiters, m_pos + m_count);
+  if (m_pos == nonstd::string_view::npos) {
+    m_pos = m_string.size();
+    m_count = 0;
+  } else {
+    m_count = m_string.substr(m_pos).find_first_of(m_delimiters);
+    if (m_count == nonstd::string_view::npos) {
+      m_count = m_string.size() - m_pos;
+    }
+  }
+  return *this;
+}
+
+} // namespace util
diff --git a/src/util/Tokenizer.hpp b/src/util/Tokenizer.hpp
new file mode 100644 (file)
index 0000000..3ee3512
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright (C) 2021 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program 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 General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include <third_party/nonstd/optional.hpp>
+#include <third_party/nonstd/string_view.hpp>
+
+namespace util {
+
+class Tokenizer
+{
+public:
+  Tokenizer(nonstd::string_view string, const char* delimiters);
+
+  class Iterator
+  {
+  public:
+    Iterator(nonstd::string_view string,
+             const char* delimiters,
+             size_t start_pos);
+
+    Iterator operator++();
+    bool operator!=(const Iterator& other) const;
+    nonstd::string_view operator*() const;
+
+  private:
+    const nonstd::string_view m_string;
+    const char* const m_delimiters;
+    size_t m_pos;
+    size_t m_count = 0;
+  };
+
+  Iterator begin();
+  Iterator end();
+
+private:
+  const nonstd::string_view m_string;
+  const char* const m_delimiters;
+};
+
+inline Tokenizer::Tokenizer(const nonstd::string_view string,
+                            const char* const delimiters)
+  : m_string(string),
+    m_delimiters(delimiters)
+{
+  assert(delimiters != nullptr && delimiters[0] != '\0');
+}
+
+inline Tokenizer::Iterator::Iterator(const nonstd::string_view string,
+                                     const char* const delimiters,
+                                     const size_t start_pos)
+  : m_string(string),
+    m_delimiters(delimiters),
+    m_pos(start_pos)
+{
+  ++*this;
+}
+
+inline bool
+Tokenizer::Iterator::operator!=(const Iterator& other) const
+{
+  assert(m_string.data() == other.m_string.data());
+  assert(m_delimiters == other.m_delimiters);
+  return m_pos != other.m_pos || m_count != other.m_count;
+}
+
+inline nonstd::string_view
+Tokenizer::Iterator::operator*() const
+{
+  assert(m_pos < m_string.size());
+  return m_string.substr(m_pos, m_count);
+}
+
+inline Tokenizer::Iterator
+Tokenizer::begin()
+{
+  return Iterator(m_string, m_delimiters, 0);
+}
+
+inline Tokenizer::Iterator
+Tokenizer::end()
+{
+  return Iterator(m_string, m_delimiters, m_string.size());
+}
+
+} // namespace util
index 48cf058ecb9212659128fe571e34545c20280c9f..fc1ddfa465fdab01ec16d132450882c4bc6123aa 100644 (file)
@@ -20,7 +20,9 @@ set(
   test_argprocessing.cpp
   test_ccache.cpp
   test_compopt.cpp
-  test_hashutil.cpp)
+  test_hashutil.cpp
+  test_util_Tokenizer.cpp
+)
 
 if(INODE_CACHE_SUPPORTED)
   list(APPEND source_files test_InodeCache.cpp)
index c3b24246ec4830de833a203a8a08c51184b09b28..87660f3b826fcccf61abc34a9df3940184b88f7e 100644 (file)
@@ -860,87 +860,8 @@ TEST_CASE("Util::same_program_name")
 #endif
 }
 
-TEST_CASE("Util::split_into_views")
-{
-  {
-    CHECK(Util::split_into_views("", "/").empty());
-  }
-  {
-    CHECK(Util::split_into_views("///", "/").empty());
-  }
-  {
-    auto s = Util::split_into_views("a/b", "/");
-    REQUIRE(s.size() == 2);
-    CHECK(s.at(0) == "a");
-    CHECK(s.at(1) == "b");
-  }
-  {
-    auto s = Util::split_into_views("a/b", "x");
-    REQUIRE(s.size() == 1);
-    CHECK(s.at(0) == "a/b");
-  }
-  {
-    auto s = Util::split_into_views("a/b:c", "/:");
-    REQUIRE(s.size() == 3);
-    CHECK(s.at(0) == "a");
-    CHECK(s.at(1) == "b");
-    CHECK(s.at(2) == "c");
-  }
-  {
-    auto s = Util::split_into_views(":a//b..:.c/:/.", "/:.");
-    REQUIRE(s.size() == 3);
-    CHECK(s.at(0) == "a");
-    CHECK(s.at(1) == "b");
-    CHECK(s.at(2) == "c");
-  }
-  {
-    auto s = Util::split_into_views(".0.1.2.3.4.5.6.7.8.9.", "/:.+_abcdef");
-    REQUIRE(s.size() == 10);
-    CHECK(s.at(0) == "0");
-    CHECK(s.at(9) == "9");
-  }
-}
-
-TEST_CASE("Util::split_into_strings")
-{
-  {
-    CHECK(Util::split_into_strings("", "/").empty());
-  }
-  {
-    CHECK(Util::split_into_strings("///", "/").empty());
-  }
-  {
-    auto s = Util::split_into_strings("a/b", "/");
-    REQUIRE(s.size() == 2);
-    CHECK(s.at(0) == "a");
-    CHECK(s.at(1) == "b");
-  }
-  {
-    auto s = Util::split_into_strings("a/b", "x");
-    REQUIRE(s.size() == 1);
-    CHECK(s.at(0) == "a/b");
-  }
-  {
-    auto s = Util::split_into_strings("a/b:c", "/:");
-    REQUIRE(s.size() == 3);
-    CHECK(s.at(0) == "a");
-    CHECK(s.at(1) == "b");
-    CHECK(s.at(2) == "c");
-  }
-  {
-    auto s = Util::split_into_strings(":a//b..:.c/:/.", "/:.");
-    REQUIRE(s.size() == 3);
-    CHECK(s.at(0) == "a");
-    CHECK(s.at(1) == "b");
-    CHECK(s.at(2) == "c");
-  }
-  {
-    auto s = Util::split_into_strings(".0.1.2.3.4.5.6.7.8.9.", "/:.+_abcdef");
-    REQUIRE(s.size() == 10);
-    CHECK(s.at(0) == "0");
-    CHECK(s.at(9) == "9");
-  }
-}
+// Util::split_into_strings and Util::split_into_views are tested implicitly in
+// test_Tokenizer.cpp.
 
 TEST_CASE("Util::starts_with")
 {
diff --git a/unittest/test_util_Tokenizer.cpp b/unittest/test_util_Tokenizer.cpp
new file mode 100644 (file)
index 0000000..9a7801f
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright (C) 2021 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program 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 General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "../src/Util.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_CASE("util::Tokenizer")
+{
+  CHECK(Util::split_into_views("", "/").empty());
+  CHECK(Util::split_into_views("///", "/").empty());
+  {
+    const auto s = Util::split_into_views("a/b", "/");
+    REQUIRE(s.size() == 2);
+    CHECK(s[0] == "a");
+    CHECK(s[1] == "b");
+  }
+  {
+    const auto s = Util::split_into_views("a/b", "x");
+    REQUIRE(s.size() == 1);
+    CHECK(s[0] == "a/b");
+  }
+  {
+    const auto s = Util::split_into_views("a/b:c", "/:");
+    REQUIRE(s.size() == 3);
+    CHECK(s[0] == "a");
+    CHECK(s[1] == "b");
+    CHECK(s[2] == "c");
+  }
+  {
+    const auto s = Util::split_into_views(":a//b..:.c/:/.", "/:.");
+    REQUIRE(s.size() == 3);
+    CHECK(s[0] == "a");
+    CHECK(s[1] == "b");
+    CHECK(s[2] == "c");
+  }
+  {
+    const auto s =
+      Util::split_into_views(".0.1.2.3.4.5.6.7.8.9.", "/:.+_abcdef");
+    REQUIRE(s.size() == 10);
+    CHECK(s[0] == "0");
+    CHECK(s[9] == "9");
+  }
+}