From b806b0a97f8436e33380eb3d579f86d25085906a Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Thu, 30 Jun 2022 17:43:12 +0200
Subject: [PATCH] util/StringStrip: add std::string_view overloads

---
 src/util/StringStrip.cxx      | 30 ++++++++++++++++++++++++++-
 src/util/StringStrip.hxx      | 18 ++++++++++++----
 test/util/TestStringStrip.cxx | 39 +++++++++++++++++++++++++++++++++++
 test/util/meson.build         |  1 +
 4 files changed, 83 insertions(+), 5 deletions(-)
 create mode 100644 test/util/TestStringStrip.cxx

diff --git a/src/util/StringStrip.cxx b/src/util/StringStrip.cxx
index fb201cac8..9ad65cee0 100644
--- a/src/util/StringStrip.cxx
+++ b/src/util/StringStrip.cxx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009-2021 Max Kellermann <max.kellermann@gmail.com>
+ * Copyright 2009-2022 Max Kellermann <max.kellermann@gmail.com>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,6 +30,7 @@
 #include "StringStrip.hxx"
 #include "CharUtil.hxx"
 
+#include <algorithm>
 #include <cstring>
 
 const char *
@@ -50,6 +51,18 @@ StripLeft(const char *p, const char *end) noexcept
 	return p;
 }
 
+std::string_view
+StripLeft(const std::string_view s) noexcept
+{
+	auto i = std::find_if_not(s.begin(), s.end(),
+				  [](auto ch){ return IsWhitespaceOrNull(ch); });
+
+	return {
+		i,
+		s.end(),
+	};
+}
+
 const char *
 StripRight(const char *p, const char *end) noexcept
 {
@@ -76,6 +89,15 @@ StripRight(char *p) noexcept
 	p[new_length] = 0;
 }
 
+std::string_view
+StripRight(std::string_view s) noexcept
+{
+	auto i = std::find_if_not(s.rbegin(), s.rend(),
+				  [](auto ch){ return IsWhitespaceOrNull(ch); });
+
+	return s.substr(0, std::distance(i, s.rend()));
+}
+
 char *
 Strip(char *p) noexcept
 {
@@ -83,3 +105,9 @@ Strip(char *p) noexcept
 	StripRight(p);
 	return p;
 }
+
+std::string_view
+Strip(std::string_view s) noexcept
+{
+	return StripRight(StripLeft(s));
+}
diff --git a/src/util/StringStrip.hxx b/src/util/StringStrip.hxx
index e2786fc91..d2400941a 100644
--- a/src/util/StringStrip.hxx
+++ b/src/util/StringStrip.hxx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009-2021 Max Kellermann <max.kellermann@gmail.com>
+ * Copyright 2009-2022 Max Kellermann <max.kellermann@gmail.com>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,10 +27,10 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef STRING_STRIP_HXX
-#define STRING_STRIP_HXX
+#pragma once
 
 #include <cstddef>
+#include <string_view>
 
 /**
  * Skips whitespace at the beginning of the string, and returns the
@@ -57,6 +57,10 @@ StripLeft(char *p) noexcept
 const char *
 StripLeft(const char *p, const char *end) noexcept;
 
+[[gnu::pure]]
+std::string_view
+StripLeft(std::string_view s) noexcept;
+
 /**
  * Determine the string's end as if it was stripped on the right side.
  */
@@ -90,6 +94,10 @@ StripRight(const char *p, std::size_t length) noexcept;
 void
 StripRight(char *p) noexcept;
 
+[[gnu::pure]]
+std::string_view
+StripRight(std::string_view s) noexcept;
+
 /**
  * Skip whitespace at the beginning and terminate the string after the
  * last non-whitespace character.
@@ -98,4 +106,6 @@ StripRight(char *p) noexcept;
 char *
 Strip(char *p) noexcept;
 
-#endif
+[[gnu::pure]]
+std::string_view
+Strip(std::string_view s) noexcept;
diff --git a/test/util/TestStringStrip.cxx b/test/util/TestStringStrip.cxx
new file mode 100644
index 000000000..57bb70452
--- /dev/null
+++ b/test/util/TestStringStrip.cxx
@@ -0,0 +1,39 @@
+/*
+ * Unit tests for src/util/
+ */
+
+#include "util/StringStrip.hxx"
+
+#include <gtest/gtest.h>
+
+using std::string_view_literals::operator""sv;
+
+TEST(StringStrip, StripLeft)
+{
+	EXPECT_EQ(StripLeft(""sv), ""sv);
+	EXPECT_EQ(StripLeft(" "sv), ""sv);
+	EXPECT_EQ(StripLeft("\t"sv), ""sv);
+	EXPECT_EQ(StripLeft("\0"sv), ""sv);
+	EXPECT_EQ(StripLeft(" a "sv), "a "sv);
+	EXPECT_EQ(StripLeft("\0a\0"sv), "a\0"sv);
+}
+
+TEST(StringStrip, StripRight)
+{
+	EXPECT_EQ(StripRight(""sv), ""sv);
+	EXPECT_EQ(StripRight(" "sv), ""sv);
+	EXPECT_EQ(StripRight("\t"sv), ""sv);
+	EXPECT_EQ(StripRight("\0"sv), ""sv);
+	EXPECT_EQ(StripRight(" a "sv), " a"sv);
+	EXPECT_EQ(StripRight("\0a\0"sv), "\0a"sv);
+}
+
+TEST(StringStrip, Strip)
+{
+	EXPECT_EQ(Strip(""sv), ""sv);
+	EXPECT_EQ(Strip(" "sv), ""sv);
+	EXPECT_EQ(Strip("\t"sv), ""sv);
+	EXPECT_EQ(Strip("\0"sv), ""sv);
+	EXPECT_EQ(Strip(" a "sv), "a"sv);
+	EXPECT_EQ(Strip("\0a\0"sv), "a"sv);
+}
diff --git a/test/util/meson.build b/test/util/meson.build
index cf0865d9b..23ebab3b9 100644
--- a/test/util/meson.build
+++ b/test/util/meson.build
@@ -8,6 +8,7 @@ test(
     'TestIntrusiveList.cxx',
     'TestMimeType.cxx',
     'TestSplitString.cxx',
+    'TestStringStrip.cxx',
     'TestTemplateString.cxx',
     'TestUriExtract.cxx',
     'TestUriQueryParser.cxx',