summaryrefslogtreecommitdiff
path: root/base
diff options
context:
space:
mode:
authorDan Albert <danalbert@google.com>2015-03-13 22:57:40 -0700
committerDan Albert <danalbert@google.com>2015-03-16 20:03:03 -0700
commit0f1e54491ceace259d156313e583aeffbcd9b444 (patch)
tree3cf78554c63b23d168dff547e721083a0a693dc1 /base
parentaa9eca2955cebf7d7e6b049750ed088329e615ed (diff)
Add common string utilities to libbase.
These are useful outside of ART. Nothing changed (aside from fixing Trim to not segfault on empty strings), so ART should be able to move to using these. Change-Id: Id026ebffe8d31f784a91834786ab189680b13a0f
Diffstat (limited to 'base')
-rw-r--r--base/Android.mk2
-rw-r--r--base/include/base/strings.h47
-rw-r--r--base/strings.cpp111
-rw-r--r--base/strings_test.cpp142
4 files changed, 302 insertions, 0 deletions
diff --git a/base/Android.mk b/base/Android.mk
index 3b64ab0cd..0e1a9b61b 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -19,10 +19,12 @@ LOCAL_PATH := $(call my-dir)
libbase_src_files := \
file.cpp \
stringprintf.cpp \
+ strings.cpp \
libbase_test_src_files := \
file_test.cpp \
stringprintf_test.cpp \
+ strings_test.cpp \
libbase_cppflags := \
-Wall \
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
new file mode 100644
index 000000000..5ddfbbd0d
--- /dev/null
+++ b/base/include/base/strings.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef BASE_STRINGS_H
+#define BASE_STRINGS_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace base {
+
+// Splits a string using the given separator character into a vector of strings.
+// Empty strings will be omitted.
+void Split(const std::string& s, char separator,
+ std::vector<std::string>* result);
+
+// Trims whitespace off both ends of the given string.
+std::string Trim(const std::string& s);
+
+// Joins a vector of strings into a single string, using the given separator.
+template <typename StringT>
+std::string Join(const std::vector<StringT>& strings, char separator);
+
+// Tests whether 's' starts with 'prefix'.
+bool StartsWith(const std::string& s, const char* prefix);
+
+// Tests whether 's' ends with 'suffix'.
+bool EndsWith(const std::string& s, const char* suffix);
+
+} // namespace base
+} // namespace android
+
+#endif // BASE_STRINGS_H
diff --git a/base/strings.cpp b/base/strings.cpp
new file mode 100644
index 000000000..224a46f5b
--- /dev/null
+++ b/base/strings.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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 "base/strings.h"
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace base {
+
+void Split(const std::string& s, char separator,
+ std::vector<std::string>* result) {
+ const char* p = s.data();
+ const char* end = p + s.size();
+ while (p != end) {
+ if (*p == separator) {
+ ++p;
+ } else {
+ const char* start = p;
+ while (++p != end && *p != separator) {
+ // Skip to the next occurrence of the separator.
+ }
+ result->push_back(std::string(start, p - start));
+ }
+ }
+}
+
+std::string Trim(const std::string& s) {
+ std::string result;
+
+ if (s.size() == 0) {
+ return result;
+ }
+
+ size_t start_index = 0;
+ size_t end_index = s.size() - 1;
+
+ // Skip initial whitespace.
+ while (start_index < s.size()) {
+ if (!isspace(s[start_index])) {
+ break;
+ }
+ start_index++;
+ }
+
+ // Skip terminating whitespace.
+ while (end_index >= start_index) {
+ if (!isspace(s[end_index])) {
+ break;
+ }
+ end_index--;
+ }
+
+ // All spaces, no beef.
+ if (end_index < start_index) {
+ return "";
+ }
+ // Start_index is the first non-space, end_index is the last one.
+ return s.substr(start_index, end_index - start_index + 1);
+}
+
+template <typename StringT>
+std::string Join(const std::vector<StringT>& strings, char separator) {
+ if (strings.empty()) {
+ return "";
+ }
+
+ std::string result(strings[0]);
+ for (size_t i = 1; i < strings.size(); ++i) {
+ result += separator;
+ result += strings[i];
+ }
+ return result;
+}
+
+// Explicit instantiations.
+template std::string Join<std::string>(const std::vector<std::string>& strings,
+ char separator);
+template std::string Join<const char*>(const std::vector<const char*>& strings,
+ char separator);
+
+bool StartsWith(const std::string& s, const char* prefix) {
+ return s.compare(0, strlen(prefix), prefix) == 0;
+}
+
+bool EndsWith(const std::string& s, const char* suffix) {
+ size_t suffix_length = strlen(suffix);
+ size_t string_length = s.size();
+ if (suffix_length > string_length) {
+ return false;
+ }
+ size_t offset = string_length - suffix_length;
+ return s.compare(offset, suffix_length, suffix) == 0;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
new file mode 100644
index 000000000..824598dcf
--- /dev/null
+++ b/base/strings_test.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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 "base/strings.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+TEST(strings, split_empty) {
+ std::vector<std::string> parts;
+ android::base::Split("", '\0', &parts);
+ ASSERT_EQ(0U, parts.size());
+}
+
+TEST(strings, split_single) {
+ std::vector<std::string> parts;
+ android::base::Split("foo", ',', &parts);
+ ASSERT_EQ(1U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+}
+
+TEST(strings, split_simple) {
+ std::vector<std::string> parts;
+ android::base::Split("foo,bar,baz", ',', &parts);
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("baz", parts[2]);
+}
+
+TEST(strings, split_with_empty_part) {
+ std::vector<std::string> parts;
+ android::base::Split("foo,,bar", ',', &parts);
+ ASSERT_EQ(2U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+}
+
+TEST(strings, trim_empty) {
+ ASSERT_EQ("", android::base::Trim(""));
+}
+
+TEST(strings, trim_already_trimmed) {
+ ASSERT_EQ("foo", android::base::Trim("foo"));
+}
+
+TEST(strings, trim_left) {
+ ASSERT_EQ("foo", android::base::Trim(" foo"));
+}
+
+TEST(strings, trim_right) {
+ ASSERT_EQ("foo", android::base::Trim("foo "));
+}
+
+TEST(strings, trim_both) {
+ ASSERT_EQ("foo", android::base::Trim(" foo "));
+}
+
+TEST(strings, trim_no_trim_middle) {
+ ASSERT_EQ("foo bar", android::base::Trim("foo bar"));
+}
+
+TEST(strings, trim_other_whitespace) {
+ ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f"));
+}
+
+TEST(strings, join_nothing) {
+ std::vector<std::string> list = {};
+ ASSERT_EQ("", android::base::Join(list, ','));
+}
+
+TEST(strings, join_single) {
+ std::vector<std::string> list = {"foo"};
+ ASSERT_EQ("foo", android::base::Join(list, ','));
+}
+
+TEST(strings, join_simple) {
+ std::vector<std::string> list = {"foo", "bar", "baz"};
+ ASSERT_EQ("foo,bar,baz", android::base::Join(list, ','));
+}
+
+TEST(strings, join_separator_in_vector) {
+ std::vector<std::string> list = {",", ","};
+ ASSERT_EQ(",,,", android::base::Join(list, ','));
+}
+
+TEST(strings, startswith_empty) {
+ ASSERT_FALSE(android::base::StartsWith("", "foo"));
+ ASSERT_TRUE(android::base::StartsWith("", ""));
+}
+
+TEST(strings, startswith_simple) {
+ ASSERT_TRUE(android::base::StartsWith("foo", ""));
+ ASSERT_TRUE(android::base::StartsWith("foo", "f"));
+ ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
+ ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
+}
+
+TEST(strings, startswith_prefix_too_long) {
+ ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
+}
+
+TEST(strings, startswith_contains_prefix) {
+ ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
+ ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
+}
+
+TEST(strings, endswith_empty) {
+ ASSERT_FALSE(android::base::EndsWith("", "foo"));
+ ASSERT_TRUE(android::base::EndsWith("", ""));
+}
+
+TEST(strings, endswith_simple) {
+ ASSERT_TRUE(android::base::EndsWith("foo", ""));
+ ASSERT_TRUE(android::base::EndsWith("foo", "o"));
+ ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
+ ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
+}
+
+TEST(strings, endswith_prefix_too_long) {
+ ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
+}
+
+TEST(strings, endswith_contains_prefix) {
+ ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
+ ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
+}