libweave: Partially copy string_utils from libchromeos
To move dependency on libchromeos.
BUG=brillo:1257
TEST=`FEATURES=test emerge-gizmo libweave buffet
Change-Id: Ie9e4bb1a754373c26ed6497cdb3ab03df8a78d87
Reviewed-on: https://chromium-review.googlesource.com/293613
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/libweave.gyp b/libweave/libweave.gyp
index 8903b28..3ff064b 100644
--- a/libweave/libweave.gyp
+++ b/libweave/libweave.gyp
@@ -75,6 +75,7 @@
'src/states/state_manager.cc',
'src/states/state_package.cc',
'src/storage_impls.cc',
+ 'src/string_utils.cc',
'src/utils.cc',
],
},
@@ -150,6 +151,7 @@
'src/states/state_change_queue_unittest.cc',
'src/states/state_manager_unittest.cc',
'src/states/state_package_unittest.cc',
+ 'src/string_utils_unittest.cc',
'src/weave_testrunner.cc',
],
},
diff --git a/libweave/src/buffet_config.cc b/libweave/src/buffet_config.cc
index 97ae16e..2698630 100644
--- a/libweave/src/buffet_config.cc
+++ b/libweave/src/buffet_config.cc
@@ -9,11 +9,11 @@
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
-#include <chromeos/strings/string_utils.h>
#include <weave/enum_to_string.h>
#include "libweave/src/storage_impls.h"
#include "libweave/src/storage_interface.h"
+#include "libweave/src/string_utils.h"
namespace {
@@ -195,8 +195,7 @@
std::string modes_str;
if (store.GetString(config_keys::kPairingModes, &modes_str)) {
std::set<PairingType> pairing_modes;
- for (const std::string& mode :
- chromeos::string_utils::Split(modes_str, ",", true, true)) {
+ for (const std::string& mode : Split(modes_str, ",", true, true)) {
PairingType pairing_mode;
CHECK(StringToEnum(mode, &pairing_mode));
pairing_modes.insert(pairing_mode);
diff --git a/libweave/src/commands/command_definition.cc b/libweave/src/commands/command_definition.cc
index e14a182..66a487c 100644
--- a/libweave/src/commands/command_definition.cc
+++ b/libweave/src/commands/command_definition.cc
@@ -7,9 +7,9 @@
#include <vector>
#include <chromeos/errors/error.h>
-#include <chromeos/strings/string_utils.h>
#include "libweave/src/commands/schema_constants.h"
+#include "libweave/src/string_utils.h"
namespace weave {
@@ -29,7 +29,7 @@
if (str == commands::attributes::kCommand_Visibility_None)
return true;
- for (const std::string& value : chromeos::string_utils::Split(str, ",")) {
+ for (const std::string& value : Split(str, ",", true, true)) {
if (value == commands::attributes::kCommand_Visibility_Local) {
local = true;
} else if (value == commands::attributes::kCommand_Visibility_Cloud) {
diff --git a/libweave/src/commands/command_dictionary.cc b/libweave/src/commands/command_dictionary.cc
index 02c3ddd..2f36850 100644
--- a/libweave/src/commands/command_dictionary.cc
+++ b/libweave/src/commands/command_dictionary.cc
@@ -5,11 +5,11 @@
#include "libweave/src/commands/command_dictionary.h"
#include <base/values.h>
-#include <chromeos/strings/string_utils.h>
#include <weave/enum_to_string.h>
#include "libweave/src/commands/command_definition.h"
#include "libweave/src/commands/schema_constants.h"
+#include "libweave/src/string_utils.h"
namespace weave {
@@ -64,8 +64,7 @@
return false;
}
// Construct the compound command name as "pkg_name.cmd_name".
- std::string full_command_name =
- chromeos::string_utils::Join(".", package_name, command_name);
+ std::string full_command_name = Join(".", package_name, command_name);
const ObjectSchema* base_parameters_def = nullptr;
const ObjectSchema* base_progress_def = nullptr;
@@ -226,9 +225,10 @@
CHECK(parameters);
// Progress and results are not part of public commandDefs.
- auto cmd_name_parts = chromeos::string_utils::SplitAtFirst(pair.first, ".");
- std::string package_name = cmd_name_parts.first;
- std::string command_name = cmd_name_parts.second;
+ auto parts = SplitAtFirst(pair.first, ".", true);
+ const std::string& package_name = parts.first;
+ const std::string& command_name = parts.second;
+
base::DictionaryValue* package = nullptr;
if (!dict->GetDictionaryWithoutPathExpansion(package_name, &package)) {
// If this is the first time we encounter this package, create a JSON
diff --git a/libweave/src/commands/command_queue_unittest.cc b/libweave/src/commands/command_queue_unittest.cc
index cd91c3d..84eeb3d 100644
--- a/libweave/src/commands/command_queue_unittest.cc
+++ b/libweave/src/commands/command_queue_unittest.cc
@@ -10,11 +10,11 @@
#include <base/bind.h>
#include <base/memory/weak_ptr.h>
-#include <chromeos/strings/string_utils.h>
#include <gtest/gtest.h>
#include "libweave/src/commands/command_definition.h"
#include "libweave/src/commands/object_schema.h"
+#include "libweave/src/string_utils.h"
namespace weave {
@@ -72,7 +72,6 @@
// Get the comma-separated list of command IDs currently accumulated in the
// command queue_.
std::string GetIDs() const {
- using chromeos::string_utils::Join;
return Join(",", std::vector<std::string>(ids_.begin(), ids_.end()));
}
@@ -136,8 +135,8 @@
queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
queue_.Add(CreateDummyCommandInstance("base.reboot", id2));
std::set<std::string> ids{id1, id2}; // Make sure they are sorted properly.
- std::string expected_set = chromeos::string_utils::Join(
- ",", std::vector<std::string>(ids.begin(), ids.end()));
+ std::string expected_set =
+ Join(",", std::vector<std::string>(ids.begin(), ids.end()));
EXPECT_EQ(expected_set, dispatch.GetIDs());
Remove(id1);
EXPECT_EQ(id2, dispatch.GetIDs());
diff --git a/libweave/src/commands/object_schema.cc b/libweave/src/commands/object_schema.cc
index b08d143..b2c6307 100644
--- a/libweave/src/commands/object_schema.cc
+++ b/libweave/src/commands/object_schema.cc
@@ -14,6 +14,7 @@
#include "libweave/src/commands/prop_types.h"
#include "libweave/src/commands/prop_values.h"
#include "libweave/src/commands/schema_constants.h"
+#include "libweave/src/string_utils.h"
namespace weave {
@@ -23,10 +24,10 @@
// Generates an error if the string identifies an unknown type.
std::unique_ptr<PropType> CreatePropType(const std::string& type_name,
chromeos::ErrorPtr* error) {
- std::string primary_type;
- std::string array_type;
- chromeos::string_utils::SplitAtFirst(type_name, ".", &primary_type,
- &array_type, false);
+ auto parts = SplitAtFirst(type_name, ".", false);
+ const std::string& primary_type = parts.first;
+ const std::string& array_type = parts.second;
+
std::unique_ptr<PropType> prop;
ValueType type;
if (PropType::GetTypeFromTypeString(primary_type, &type)) {
diff --git a/libweave/src/commands/prop_constraints.cc b/libweave/src/commands/prop_constraints.cc
index 4f730f7..0b77ec5 100644
--- a/libweave/src/commands/prop_constraints.cc
+++ b/libweave/src/commands/prop_constraints.cc
@@ -8,6 +8,7 @@
#include "libweave/src/commands/prop_values.h"
#include "libweave/src/commands/schema_constants.h"
+#include "libweave/src/string_utils.h"
namespace weave {
@@ -54,11 +55,10 @@
bool Constraint::ReportErrorNotOneOf(chromeos::ErrorPtr* error,
const std::string& val,
const std::vector<std::string>& values) {
- chromeos::Error::AddToPrintf(
- error, FROM_HERE, errors::commands::kDomain,
- errors::commands::kOutOfRange,
- "Value %s is invalid. Expected one of [%s]", val.c_str(),
- chromeos::string_utils::Join(",", values).c_str());
+ chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+ errors::commands::kOutOfRange,
+ "Value %s is invalid. Expected one of [%s]",
+ val.c_str(), Join(",", values).c_str());
return false;
}
diff --git a/libweave/src/commands/prop_constraints.h b/libweave/src/commands/prop_constraints.h
index 098f97c..c0744d5 100644
--- a/libweave/src/commands/prop_constraints.h
+++ b/libweave/src/commands/prop_constraints.h
@@ -12,11 +12,11 @@
#include <base/macros.h>
#include <base/values.h>
#include <chromeos/errors/error.h>
-#include <chromeos/strings/string_utils.h>
#include "libweave/src/commands/prop_values.h"
#include "libweave/src/commands/schema_constants.h"
#include "libweave/src/commands/schema_utils.h"
+#include "libweave/src/string_utils.h"
namespace weave {
@@ -134,9 +134,8 @@
chromeos::ErrorPtr* error) const override {
const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue();
if (v < this->limit_.value) {
- return this->ReportErrorLessThan(
- error, chromeos::string_utils::ToString(v),
- chromeos::string_utils::ToString(this->limit_.value));
+ return this->ReportErrorLessThan(error, std::to_string(v),
+ std::to_string(this->limit_.value));
}
return true;
}
@@ -176,9 +175,8 @@
chromeos::ErrorPtr* error) const override {
const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue();
if (v > this->limit_.value)
- return this->ReportErrorGreaterThan(
- error, chromeos::string_utils::ToString(v),
- chromeos::string_utils::ToString(this->limit_.value));
+ return this->ReportErrorGreaterThan(error, std::to_string(v),
+ std::to_string(this->limit_.value));
return true;
}
diff --git a/libweave/src/commands/prop_types.cc b/libweave/src/commands/prop_types.cc
index 6daabc0..5a77610 100644
--- a/libweave/src/commands/prop_types.cc
+++ b/libweave/src/commands/prop_types.cc
@@ -11,7 +11,6 @@
#include <base/json/json_writer.h>
#include <base/logging.h>
#include <base/values.h>
-#include <chromeos/strings/string_utils.h>
#include "libweave/src/commands/object_schema.h"
#include "libweave/src/commands/prop_values.h"
diff --git a/libweave/src/data_encoding.cc b/libweave/src/data_encoding.cc
index 419e2d4..28a63ea 100644
--- a/libweave/src/data_encoding.cc
+++ b/libweave/src/data_encoding.cc
@@ -9,8 +9,8 @@
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/strings/string_utils.h>
+#include "libweave/src/string_utils.h"
#include "libweave/third_party/modp_b64/modp_b64/modp_b64.h"
namespace weave {
@@ -90,17 +90,16 @@
for (const auto& p : params) {
std::string key = UrlEncode(p.first.c_str(), encodeSpaceAsPlus);
std::string value = UrlEncode(p.second.c_str(), encodeSpaceAsPlus);
- pairs.push_back(chromeos::string_utils::Join("=", key, value));
+ pairs.push_back(Join("=", key, value));
}
- return chromeos::string_utils::Join("&", pairs);
+ return Join("&", pairs);
}
WebParamList WebParamsDecode(const std::string& data) {
WebParamList result;
- std::vector<std::string> params = chromeos::string_utils::Split(data, "&");
- for (const auto& p : params) {
- auto pair = chromeos::string_utils::SplitAtFirst(p, "=");
+ for (const auto& p : Split(data, "&", true, true)) {
+ auto pair = SplitAtFirst(p, "=", true);
result.emplace_back(UrlDecode(pair.first.c_str()),
UrlDecode(pair.second.c_str()));
}
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc
index a799c41..3573cb8 100644
--- a/libweave/src/device_registration_info.cc
+++ b/libweave/src/device_registration_info.cc
@@ -16,7 +16,6 @@
#include <base/strings/string_number_conversions.h>
#include <base/values.h>
#include <chromeos/key_value_store.h>
-#include <chromeos/strings/string_utils.h>
#include <weave/http_client.h>
#include <weave/network.h>
#include <weave/task_runner.h>
@@ -31,6 +30,7 @@
#include "libweave/src/json_error_codes.h"
#include "libweave/src/notification/xmpp_channel.h"
#include "libweave/src/states/state_manager.h"
+#include "libweave/src/string_utils.h"
#include "libweave/src/utils.h"
namespace weave {
@@ -175,9 +175,8 @@
chromeos::ErrorPtr* error) {
// Make sure we have a correct content type. Do not try to parse
// binary files, or HTML output. Limit to application/json and text/plain.
- auto content_type =
- chromeos::string_utils::SplitAtFirst(response.GetContentType(), ";")
- .first;
+ std::string content_type =
+ SplitAtFirst(response.GetContentType(), ";", true).first;
if (content_type != http::kJson && content_type != http::kPlain) {
chromeos::Error::AddTo(error, FROM_HERE, errors::json::kDomain,
@@ -890,8 +889,7 @@
EnumToString(CommandStatus::kAborted));
if (error) {
command_patch.SetString(commands::attributes::kCommand_ErrorCode,
- chromeos::string_utils::Join(
- ":", error->GetDomain(), error->GetCode()));
+ Join(":", error->GetDomain(), error->GetCode()));
std::vector<std::string> messages;
const chromeos::Error* current_error = error.get();
while (current_error) {
@@ -899,7 +897,7 @@
current_error = current_error->GetInnerError();
}
command_patch.SetString(commands::attributes::kCommand_ErrorMessage,
- chromeos::string_utils::Join(";", messages));
+ Join(";", messages));
}
UpdateCommand(command_id, command_patch, base::Bind(&base::DoNothing),
base::Bind(&base::DoNothing));
diff --git a/libweave/src/notification/xml_node.cc b/libweave/src/notification/xml_node.cc
index 6550ddc..f8b6c52 100644
--- a/libweave/src/notification/xml_node.cc
+++ b/libweave/src/notification/xml_node.cc
@@ -5,7 +5,8 @@
#include "libweave/src/notification/xml_node.h"
#include <base/strings/stringprintf.h>
-#include <chromeos/strings/string_utils.h>
+
+#include "libweave/src/string_utils.h"
namespace weave {
@@ -61,10 +62,9 @@
const std::string& name_path,
bool recursive,
std::vector<const XmlNode*>* children) const {
- std::string name;
- std::string rest_of_path;
- chromeos::string_utils::SplitAtFirst(name_path, "/", &name, &rest_of_path,
- false);
+ auto parts = SplitAtFirst(name_path, "/", false);
+ const std::string& name = parts.first;
+ const std::string& rest_of_path = parts.second;
for (const auto& child : children_) {
const XmlNode* found_node = nullptr;
if (child->name() == name) {
diff --git a/libweave/src/privet/privet_handler.cc b/libweave/src/privet/privet_handler.cc
index 04fc8d2..9f22750 100644
--- a/libweave/src/privet/privet_handler.cc
+++ b/libweave/src/privet/privet_handler.cc
@@ -15,7 +15,6 @@
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <base/values.h>
-#include <chromeos/strings/string_utils.h>
#include <weave/enum_to_string.h>
#include "libweave/src/http_constants.h"
@@ -25,6 +24,7 @@
#include "libweave/src/privet/identity_delegate.h"
#include "libweave/src/privet/security_delegate.h"
#include "libweave/src/privet/wifi_delegate.h"
+#include "libweave/src/string_utils.h"
namespace weave {
namespace privet {
@@ -155,10 +155,7 @@
}
std::string GetAuthTokenFromAuthHeader(const std::string& auth_header) {
- std::string name;
- std::string value;
- chromeos::string_utils::SplitAtFirst(auth_header, " ", &name, &value);
- return value;
+ return SplitAtFirst(auth_header, " ", true).second;
}
std::unique_ptr<base::DictionaryValue> ErrorInfoToJson(
diff --git a/libweave/src/privet/privet_manager.cc b/libweave/src/privet/privet_manager.cc
index 7daa0f9..c64430a 100644
--- a/libweave/src/privet/privet_manager.cc
+++ b/libweave/src/privet/privet_manager.cc
@@ -17,7 +17,6 @@
#include <base/strings/string_number_conversions.h>
#include <base/values.h>
#include <chromeos/key_value_store.h>
-#include <chromeos/strings/string_utils.h>
#include <weave/network.h>
#include "libweave/src/device_registration_info.h"
@@ -27,6 +26,7 @@
#include "libweave/src/privet/device_delegate.h"
#include "libweave/src/privet/privet_handler.h"
#include "libweave/src/privet/publisher.h"
+#include "libweave/src/string_utils.h"
namespace weave {
namespace privet {
@@ -123,9 +123,7 @@
const base::DictionaryValue* dictionary = ∅
std::string content_type =
- chromeos::string_utils::SplitAtFirst(
- request.GetFirstHeader(http::kContentType), ";")
- .first;
+ SplitAtFirst(request.GetFirstHeader(http::kContentType), ";", true).first;
if (content_type == http::kJson) {
value.reset(base::JSONReader::Read(data).release());
if (value)
diff --git a/libweave/src/privet/publisher.cc b/libweave/src/privet/publisher.cc
index 367dbd8..b9f7080 100644
--- a/libweave/src/privet/publisher.cc
+++ b/libweave/src/privet/publisher.cc
@@ -7,15 +7,13 @@
#include <map>
#include <chromeos/errors/error.h>
-#include <chromeos/strings/string_utils.h>
#include <weave/mdns.h>
#include "libweave/src/privet/cloud_delegate.h"
#include "libweave/src/privet/device_delegate.h"
#include "libweave/src/privet/wifi_bootstrap_manager.h"
#include "libweave/src/privet/wifi_ssid_generator.h"
-
-using chromeos::string_utils::Join;
+#include "libweave/src/string_utils.h"
namespace weave {
namespace privet {
diff --git a/libweave/src/privet/security_manager.cc b/libweave/src/privet/security_manager.cc
index 2125744..1343a7c 100644
--- a/libweave/src/privet/security_manager.cc
+++ b/libweave/src/privet/security_manager.cc
@@ -18,13 +18,13 @@
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <chromeos/key_value_store.h>
-#include <chromeos/strings/string_utils.h>
#include <weave/task_runner.h>
#include "libweave/external/crypto/p224_spake.h"
#include "libweave/src/data_encoding.h"
#include "libweave/src/privet/constants.h"
#include "libweave/src/privet/openssl_utils.h"
+#include "libweave/src/string_utils.h"
namespace weave {
namespace privet {
@@ -49,7 +49,7 @@
// Splits string of "scope:id:time" format.
UserInfo SplitTokenData(const std::string& token, base::Time* time) {
const UserInfo kNone;
- auto parts = chromeos::string_utils::Split(token, kTokenDelimeter);
+ auto parts = Split(token, kTokenDelimeter, false, false);
if (parts.size() != 3)
return kNone;
int scope = 0;
@@ -300,7 +300,7 @@
// simultaneously and implement throttling to avoid brute force attack.
if (!on_start_.is_null()) {
on_start_.Run(session, mode,
- chromeos::string_utils::GetStringAsBytes(code));
+ std::vector<uint8_t>{code.begin(), code.end()});
}
return true;
diff --git a/libweave/src/privet/security_manager_unittest.cc b/libweave/src/privet/security_manager_unittest.cc
index a6ea288..0d13cfe 100644
--- a/libweave/src/privet/security_manager_unittest.cc
+++ b/libweave/src/privet/security_manager_unittest.cc
@@ -19,7 +19,6 @@
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <chromeos/key_value_store.h>
-#include <chromeos/strings/string_utils.h>
#include "libweave/external/crypto/p224_spake.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -102,7 +101,7 @@
std::vector<uint8_t> device_commitment;
ASSERT_TRUE(Base64Decode(device_commitment_base64, &device_commitment));
spake.ProcessMessage(
- chromeos::string_utils::GetBytesAsString(device_commitment));
+ std::string(device_commitment.begin(), device_commitment.end()));
const std::string& key = spake.GetUnverifiedKey();
std::vector<uint8_t> auth_code{
diff --git a/libweave/src/states/state_manager.cc b/libweave/src/states/state_manager.cc
index fa8f81e..6f2646b 100644
--- a/libweave/src/states/state_manager.cc
+++ b/libweave/src/states/state_manager.cc
@@ -9,11 +9,11 @@
#include <base/logging.h>
#include <base/values.h>
#include <chromeos/key_value_store.h>
-#include <chromeos/strings/string_utils.h>
#include "libweave/src/json_error_codes.h"
#include "libweave/src/states/error_codes.h"
#include "libweave/src/states/state_change_queue_interface.h"
+#include "libweave/src/string_utils.h"
#include "libweave/src/utils.h"
namespace weave {
@@ -108,10 +108,11 @@
const base::Value& value,
const base::Time& timestamp,
chromeos::ErrorPtr* error) {
- std::string package_name;
- std::string property_name;
- bool split = chromeos::string_utils::SplitAtFirst(
- full_property_name, ".", &package_name, &property_name);
+ auto parts = SplitAtFirst(full_property_name, ".", true);
+ const std::string& package_name = parts.first;
+ const std::string& property_name = parts.second;
+ const bool split = (full_property_name.find(".") != std::string::npos);
+
if (full_property_name.empty() || (split && property_name.empty())) {
chromeos::Error::AddTo(error, FROM_HERE, errors::state::kDomain,
errors::state::kPropertyNameMissing,
diff --git a/libweave/src/string_utils.cc b/libweave/src/string_utils.cc
new file mode 100644
index 0000000..d73e508
--- /dev/null
+++ b/libweave/src/string_utils.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <string.h>
+#include <utility>
+
+#include <base/strings/string_util.h>
+
+#include "libweave/src/string_utils.h"
+
+namespace weave {
+
+namespace {
+
+bool SplitAtFirst(const std::string& str,
+ const std::string& delimiter,
+ std::string* left_part,
+ std::string* right_part,
+ bool trim_whitespaces) {
+ bool delimiter_found = false;
+ std::string::size_type pos = str.find(delimiter);
+ if (pos != std::string::npos) {
+ *left_part = str.substr(0, pos);
+ *right_part = str.substr(pos + delimiter.size());
+ delimiter_found = true;
+ } else {
+ *left_part = str;
+ right_part->clear();
+ }
+
+ if (trim_whitespaces) {
+ base::TrimWhitespaceASCII(*left_part, base::TRIM_ALL, left_part);
+ base::TrimWhitespaceASCII(*right_part, base::TRIM_ALL, right_part);
+ }
+
+ return delimiter_found;
+}
+
+} // namespace
+
+std::vector<std::string> Split(const std::string& str,
+ const std::string& delimiter,
+ bool trim_whitespaces,
+ bool purge_empty_strings) {
+ std::vector<std::string> tokens;
+ if (str.empty())
+ return tokens;
+
+ for (std::string::size_type i = 0;;) {
+ const std::string::size_type pos =
+ delimiter.empty() ? (i + 1) : str.find(delimiter, i);
+ std::string tmp_str{str.substr(i, pos - i)};
+ if (trim_whitespaces)
+ base::TrimWhitespaceASCII(tmp_str, base::TRIM_ALL, &tmp_str);
+ if (!tmp_str.empty() || !purge_empty_strings)
+ tokens.emplace_back(std::move(tmp_str));
+ if (pos >= str.size())
+ break;
+ i = pos + delimiter.size();
+ }
+ return tokens;
+}
+
+std::pair<std::string, std::string> SplitAtFirst(const std::string& str,
+ const std::string& delimiter,
+ bool trim_whitespaces) {
+ std::pair<std::string, std::string> pair;
+ SplitAtFirst(str, delimiter, &pair.first, &pair.second, trim_whitespaces);
+ return pair;
+}
+
+} // namespace weave
diff --git a/libweave/src/string_utils.h b/libweave/src/string_utils.h
new file mode 100644
index 0000000..5771971
--- /dev/null
+++ b/libweave/src/string_utils.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_STRING_UTILS_H_
+#define LIBWEAVE_SRC_STRING_UTILS_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace weave {
+
+// Treats the string as a delimited list of substrings and returns the array
+// of original elements of the list.
+// |trim_whitespaces| causes each element to have all whitespaces trimmed off.
+// |purge_empty_strings| specifies whether empty elements from the original
+// string should be omitted.
+std::vector<std::string> Split(const std::string& str,
+ const std::string& delimiter,
+ bool trim_whitespaces,
+ bool purge_empty_strings);
+
+// Splits the string into two pieces at the first position of the specified
+// delimiter.
+std::pair<std::string, std::string> SplitAtFirst(const std::string& str,
+ const std::string& delimiter,
+ bool trim_whitespaces);
+
+// Joins strings into a single string separated by |delimiter|.
+template <class InputIterator>
+std::string JoinRange(const std::string& delimiter,
+ InputIterator first,
+ InputIterator last) {
+ std::string result;
+ if (first == last)
+ return result;
+ result = *first;
+ for (++first; first != last; ++first) {
+ result += delimiter;
+ result += *first;
+ }
+ return result;
+}
+
+template <class Container>
+std::string Join(const std::string& delimiter, const Container& strings) {
+ using std::begin;
+ using std::end;
+ return JoinRange(delimiter, begin(strings), end(strings));
+}
+
+inline std::string Join(const std::string& delimiter,
+ const std::string& str1,
+ const std::string& str2) {
+ return str1 + delimiter + str2;
+}
+
+} // namespace weave
+
+#endif // LIBWEAVE_SRC_STRING_UTILS_H_
diff --git a/libweave/src/string_utils_unittest.cc b/libweave/src/string_utils_unittest.cc
new file mode 100644
index 0000000..083ebb8
--- /dev/null
+++ b/libweave/src/string_utils_unittest.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "libweave/src/string_utils.h"
+
+#include <list>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace weave {
+
+TEST(StringUtils, Split) {
+ std::vector<std::string> parts;
+
+ parts = Split("", ",", false, false);
+ EXPECT_EQ(0, parts.size());
+
+ parts = Split("abc", ",", false, false);
+ EXPECT_EQ(1, parts.size());
+ EXPECT_EQ("abc", parts[0]);
+
+ parts = Split(",a,bc , d, ,e, ", ",", true, true);
+ EXPECT_EQ(4, parts.size());
+ EXPECT_EQ("a", parts[0]);
+ EXPECT_EQ("bc", parts[1]);
+ EXPECT_EQ("d", parts[2]);
+ EXPECT_EQ("e", parts[3]);
+
+ parts = Split(",a,bc , d, ,e, ", ",", false, true);
+ EXPECT_EQ(6, parts.size());
+ EXPECT_EQ("a", parts[0]);
+ EXPECT_EQ("bc ", parts[1]);
+ EXPECT_EQ(" d", parts[2]);
+ EXPECT_EQ(" ", parts[3]);
+ EXPECT_EQ("e", parts[4]);
+ EXPECT_EQ(" ", parts[5]);
+
+ parts = Split(",a,bc , d, ,e, ", ",", true, false);
+ EXPECT_EQ(7, parts.size());
+ EXPECT_EQ("", parts[0]);
+ EXPECT_EQ("a", parts[1]);
+ EXPECT_EQ("bc", parts[2]);
+ EXPECT_EQ("d", parts[3]);
+ EXPECT_EQ("", parts[4]);
+ EXPECT_EQ("e", parts[5]);
+ EXPECT_EQ("", parts[6]);
+
+ parts = Split(",a,bc , d, ,e, ", ",", false, false);
+ EXPECT_EQ(7, parts.size());
+ EXPECT_EQ("", parts[0]);
+ EXPECT_EQ("a", parts[1]);
+ EXPECT_EQ("bc ", parts[2]);
+ EXPECT_EQ(" d", parts[3]);
+ EXPECT_EQ(" ", parts[4]);
+ EXPECT_EQ("e", parts[5]);
+ EXPECT_EQ(" ", parts[6]);
+
+ parts = Split("abc:=xyz", ":=", false, false);
+ EXPECT_EQ(2, parts.size());
+ EXPECT_EQ("abc", parts[0]);
+ EXPECT_EQ("xyz", parts[1]);
+
+ parts = Split("abc", "", false, false);
+ EXPECT_EQ(3, parts.size());
+ EXPECT_EQ("a", parts[0]);
+ EXPECT_EQ("b", parts[1]);
+ EXPECT_EQ("c", parts[2]);
+}
+
+TEST(StringUtils, SplitAtFirst) {
+ std::pair<std::string, std::string> pair;
+
+ pair = SplitAtFirst(" 123 : 4 : 56 : 789 ", ":", true);
+ EXPECT_EQ("123", pair.first);
+ EXPECT_EQ("4 : 56 : 789", pair.second);
+
+ pair = SplitAtFirst(" 123 : 4 : 56 : 789 ", ":", false);
+ EXPECT_EQ(" 123 ", pair.first);
+ EXPECT_EQ(" 4 : 56 : 789 ", pair.second);
+
+ pair = SplitAtFirst("", "=", true);
+ EXPECT_EQ("", pair.first);
+ EXPECT_EQ("", pair.second);
+
+ pair = SplitAtFirst("=", "=", true);
+ EXPECT_EQ("", pair.first);
+ EXPECT_EQ("", pair.second);
+
+ pair = SplitAtFirst("a=", "=", true);
+ EXPECT_EQ("a", pair.first);
+ EXPECT_EQ("", pair.second);
+
+ pair = SplitAtFirst("abc=", "=", true);
+ EXPECT_EQ("abc", pair.first);
+ EXPECT_EQ("", pair.second);
+
+ pair = SplitAtFirst("=a", "=", true);
+ EXPECT_EQ("", pair.first);
+ EXPECT_EQ("a", pair.second);
+
+ pair = SplitAtFirst("=abc=", "=", true);
+ EXPECT_EQ("", pair.first);
+ EXPECT_EQ("abc=", pair.second);
+
+ pair = SplitAtFirst("abc", "=", true);
+ EXPECT_EQ("abc", pair.first);
+ EXPECT_EQ("", pair.second);
+
+ pair = SplitAtFirst("abc:=xyz", ":=", true);
+ EXPECT_EQ("abc", pair.first);
+ EXPECT_EQ("xyz", pair.second);
+
+ pair = SplitAtFirst("abc", "", true);
+ EXPECT_EQ("", pair.first);
+ EXPECT_EQ("abc", pair.second);
+}
+
+TEST(StringUtils, Join_String) {
+ EXPECT_EQ("", Join(",", std::vector<std::string>{}));
+ EXPECT_EQ("abc", Join(",", std::vector<std::string>{"abc"}));
+ EXPECT_EQ("abc,,xyz", Join(",", std::vector<std::string>{"abc", "", "xyz"}));
+ EXPECT_EQ("abc,defg", Join(",", std::vector<std::string>{"abc", "defg"}));
+ EXPECT_EQ("1 : 2 : 3", Join(" : ", std::vector<std::string>{"1", "2", "3"}));
+ EXPECT_EQ("1:2", Join(":", std::set<std::string>{"1", "2"}));
+ EXPECT_EQ("1:2", Join(":", std::vector<std::string>{"1", "2"}));
+ EXPECT_EQ("1:2", Join(":", std::list<std::string>{"1", "2"}));
+ EXPECT_EQ("123", Join("", std::vector<std::string>{"1", "2", "3"}));
+}
+
+TEST(StringUtils, Join_Pair) {
+ EXPECT_EQ("ab,cd", Join(",", "ab", "cd"));
+ EXPECT_EQ("key = value", Join(" = ", "key", "value"));
+}
+
+} // namespace weave