diff options
-rw-r--r-- | base/Android.mk | 2 | ||||
-rw-r--r-- | base/include/base/strings.h | 47 | ||||
-rw-r--r-- | base/strings.cpp | 111 | ||||
-rw-r--r-- | base/strings_test.cpp | 142 |
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")); +} |