From dbb360c89e3c9c45511fd1c3188847eb4cd1e99f Mon Sep 17 00:00:00 2001 From: Philip Liard Date: Fri, 25 Feb 2011 09:02:23 +0000 Subject: [PATCH] Adding light StrCat and SimpleItoa and tests. --- cpp/CMakeLists.txt | 2 + cpp/src/stringutil.cc | 186 +++++++++++++++++++++++++++++++++++++ cpp/src/stringutil.h | 100 ++++++++++++++++++++ cpp/src/stringutil_test.cc | 130 ++++++++++++++++++++++++++ 4 files changed, 418 insertions(+) create mode 100644 cpp/src/stringutil.cc create mode 100644 cpp/src/stringutil.h create mode 100644 cpp/src/stringutil_test.cc diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 797dfa6db..dbb74afb6 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -98,6 +98,7 @@ set ( "src/base/synchronization/lock.cc" "src/base/threading/thread_restrictions.cc" "src/phonenumberutil.cc" + "src/stringutil.cc" "src/utf/rune.c" ) @@ -150,6 +151,7 @@ target_link_libraries (phonenumber re2) set (TEST_SOURCES "src/phonenumberutil_test.cc" "src/run_tests.cc" + "src/stringutil_test.cc" ) add_executable (libphonenumber_test ${TEST_SOURCES}) diff --git a/cpp/src/stringutil.cc b/cpp/src/stringutil.cc new file mode 100644 index 000000000..d07b41e6f --- /dev/null +++ b/cpp/src/stringutil.cc @@ -0,0 +1,186 @@ +// Copyright (C) 2011 Google Inc. +// +// 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. + +// Author: Philippe Liard + +#include +#include +#include + +#include "stringutil.h" + +namespace i18n { +namespace phonenumbers { + +using std::stringstream; + +string operator+(const string& s, int n) { + stringstream ss; + + ss << s << n; + string result; + ss >> result; + + return result; +} + +string SimpleItoa(int n) { + stringstream ss; + + ss << n; + string result; + ss >> result; + + return result; +} + +bool TryStripPrefixString(const string& in, const string& prefix, string* out) { + assert(out); + const bool has_prefix = in.compare(0, prefix.length(), prefix) == 0; + out->assign(has_prefix ? in.substr(prefix.length()) : in); + + return has_prefix; +} + +bool HasSuffixString(const string& s, const string& suffix) { + if (s.length() < suffix.length()) { + return false; + } + return s.compare(s.length() - suffix.length(), suffix.length(), suffix) == 0; +} + +// StringHolder class + +StringHolder::StringHolder(const string& s) : + string_(&s), + cstring_(NULL), + len_(s.size()) +{} + +StringHolder::StringHolder(const char* s) : + string_(NULL), + cstring_(s), + len_(std::strlen(s)) +{} + +StringHolder::~StringHolder() {} + +// StrCat + +// Implement s += sh; (s: string, sh: StringHolder) +string& operator+=(string& lhs, const StringHolder& rhs) { + const string* const s = rhs.GetString(); + if (s) { + lhs += *s; + } else { + const char* const cs = rhs.GetCString(); + if (cs) + lhs.append(cs, rhs.Length()); + } + return lhs; +} + +string StrCat(const StringHolder& s1, const StringHolder& s2) { + string result; + result.reserve(s1.Length() + s2.Length() + 1); + + result += s1; + result += s2; + + return result; +} + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3) { + string result; + result.reserve(s1.Length() + s2.Length() + s3.Length() + 1); + + result += s1; + result += s2; + result += s3; + + return result; +} + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4) { + string result; + result.reserve(s1.Length() + s2.Length() + s3.Length() + s4.Length() + 1); + + result += s1; + result += s2; + result += s3; + result += s4; + + return result; +} + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4, + const StringHolder& s5) { + string result; + result.reserve(s1.Length() + s2.Length() + s3.Length() + s4.Length() + + s5.Length() + 1); + result += s1; + result += s2; + result += s3; + result += s4; + result += s5; + + return result; +} + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4, + const StringHolder& s5, const StringHolder& s6) { + string result; + result.reserve(s1.Length() + s2.Length() + s3.Length() + s4.Length() + + s5.Length() + s6.Length() + 1); + result += s1; + result += s2; + result += s3; + result += s4; + result += s5; + result += s6; + + return result; +} + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4, + const StringHolder& s5, const StringHolder& s6, + const StringHolder& s7, const StringHolder& s8, + const StringHolder& s9, const StringHolder& s10, + const StringHolder& s11) { + string result; + result.reserve(s1.Length() + s2.Length() + s3.Length() + s4.Length() + + s5.Length() + s6.Length() + s7.Length() + s8.Length() + + s9.Length() + s10.Length() + s11.Length()); + result += s1; + result += s2; + result += s3; + result += s4; + result += s5; + result += s6; + result += s7; + result += s8; + result += s9; + result += s10; + result += s11; + + return result; +} + +} // namespace phonenumbers +} // namespace i18n diff --git a/cpp/src/stringutil.h b/cpp/src/stringutil.h new file mode 100644 index 000000000..69ea9ce95 --- /dev/null +++ b/cpp/src/stringutil.h @@ -0,0 +1,100 @@ +// Copyright (C) 2011 Google Inc. +// +// 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. + +// Author: Philippe Liard + +#ifndef I18N_PHONENUMBERS_STRINGUTIL_H_ +#define I18N_PHONENUMBERS_STRINGUTIL_H_ + +#include +#include + +namespace i18n { +namespace phonenumbers { + +using std::string; + +// Support string("hello") + 10 +string operator+(const string& s, int n); + +// Convert integer into string +string SimpleItoa(int n); + +// Return true if 'in' starts with 'prefix' and write into 'out' +// 'in' minus 'prefix' +bool TryStripPrefixString(const string& in, const string& prefix, string* out); + +// Return true if 's' ends with 'suffix' +bool HasSuffixString(const string& s, const string& suffix); + + +// Hold a reference to a std::string or C string. +class StringHolder { +public: + // Don't make the constructors explicit to make the StrCat usage convenient. + StringHolder(const string& s); + StringHolder(const char* s); + ~StringHolder(); + + const string* GetString() const { + return string_; + } + + const char* GetCString() const { + return cstring_; + } + + size_t Length() const { + return len_; + } + +private: + const string* const string_; + const char* const cstring_; + const size_t len_; +}; + + +string& operator+=(string& lhs, const StringHolder& rhs); + + +// Efficient string concatenation + +string StrCat(const StringHolder& s1, const StringHolder& s2); + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3); + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4); + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4, + const StringHolder& s5); + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4, + const StringHolder& s5, const StringHolder& s6); + +string StrCat(const StringHolder& s1, const StringHolder& s2, + const StringHolder& s3, const StringHolder& s4, + const StringHolder& s5, const StringHolder& s6, + const StringHolder& s7, const StringHolder& s8, + const StringHolder& s9, const StringHolder& s10, + const StringHolder& s11); + +} // namespace phonenumbers +} // namespace i18n + +#endif // I18N_PHONENUMBERS_STRINGUTIL_H_ diff --git a/cpp/src/stringutil_test.cc b/cpp/src/stringutil_test.cc new file mode 100644 index 000000000..07f0e9c31 --- /dev/null +++ b/cpp/src/stringutil_test.cc @@ -0,0 +1,130 @@ +// Copyright (C) 2011 Google Inc. +// +// 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. + +// Author: Philippe Liard + +#include + +#include "stringutil.h" + +namespace i18n { +namespace phonenumbers { + +// Test operator+(const string&, int) +TEST(StringUtilTest, OperatorPlus) { + EXPECT_EQ("hello10", string("hello") + 10); +} + +// Test SimpleItoa implementation +TEST(StringUtilTest, SimpleItoa) { + EXPECT_EQ("10", SimpleItoa(10)); +} + +// Test TryStripPrefixString +TEST(StringUtilTest, TryStripPrefixString) { + string s; + + EXPECT_TRUE(TryStripPrefixString("hello world", "hello", &s)); + EXPECT_EQ(" world", s); + s.clear(); + + EXPECT_FALSE(TryStripPrefixString("hello world", "helloa", &s)); + s.clear(); + + EXPECT_TRUE(TryStripPrefixString("hello world", "", &s)); + EXPECT_EQ("hello world", s); + s.clear(); + + EXPECT_FALSE(TryStripPrefixString("", "hello", &s)); + s.clear(); +} + +// Test HasSuffixString +TEST(StringUtilTest, HasSuffixString) { + EXPECT_TRUE(HasSuffixString("hello world", "hello world")); + EXPECT_TRUE(HasSuffixString("hello world", "world")); + EXPECT_FALSE(HasSuffixString("hello world", "world!")); + EXPECT_TRUE(HasSuffixString("hello world", "")); + EXPECT_FALSE(HasSuffixString("", "hello")); +} + +// Test the StringHolder class +TEST(StringUtilTest, StringHolder) { + // Test with C string + static const char cstring[] = "aaa"; + StringHolder sh1(cstring); + EXPECT_EQ(cstring, sh1.GetCString()); + EXPECT_EQ(NULL, sh1.GetString()); + + // Test with std::string + string s = "bbb"; + StringHolder sh2(s); + EXPECT_EQ(NULL, sh2.GetCString()); + EXPECT_EQ(&s, sh2.GetString()); + + // Test GetLength() + string s2 = "hello"; + StringHolder sh3(s2); + EXPECT_EQ(5, sh3.Length()); +} + +// Test the operator+=(string& lhs, const StringHolder& rhs) implementation +TEST(StringUtilTest, OperatorPlusEquals) { + // Test with a const char* string to append + string s = "h"; + static const char append1[] = "ello"; + s += StringHolder(append1); // force StringHolder usage + + EXPECT_EQ("hello", s); + + // Test with a std::string to append + s = "h"; + string append2 = "ello"; + s += StringHolder(append2); // force StringHolder usage + + EXPECT_EQ("hello", s); +} + +// Test the StrCat implementations +TEST(StringUtilTest, StrCat) { + string s; + + // Test with 2 arguments + s = StrCat("a", "b"); + EXPECT_EQ("ab", s); + + // Test with 3 arguments + s = StrCat("a", "b", "c"); + EXPECT_EQ("abc", s); + + // Test with 4 arguments + s = StrCat("a", "b", "c", "d"); + EXPECT_EQ("abcd", s); + + // Test with 5 arguments + s = StrCat("a", "b", "c", "d", "e"); + EXPECT_EQ("abcde", s); + + // Test with 6 arguments + s = StrCat("a", "b", "c", "d", "e", "f"); + EXPECT_EQ("abcdef", s); + + + // Test with 11 arguments + s = StrCat("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"); + EXPECT_EQ("abcdefghijk", s); +} + +} // namespace phonenumbers +} // namespace i18n