+ /**
+ * Does this Range start with another range?
+ */
+ bool startsWith(const const_range_type& other) const {
+ return size() >= other.size()
+ && castToConst().subpiece(0, other.size()) == other;
+ }
+ bool startsWith(value_type c) const {
+ return !empty() && front() == c;
+ }
+
+ /**
+ * Does this Range end with another range?
+ */
+ bool endsWith(const const_range_type& other) const {
+ return size() >= other.size()
+ && castToConst().subpiece(size() - other.size()) == other;
+ }
+ bool endsWith(value_type c) const {
+ return !empty() && back() == c;
+ }
+
+ /**
+ * Remove the given prefix and return true if the range starts with the given
+ * prefix; return false otherwise.
+ */
+ bool removePrefix(const const_range_type& prefix) {
+ return startsWith(prefix) && (b_ += prefix.size(), true);
+ }
+ bool removePrefix(value_type prefix) {
+ return startsWith(prefix) && (++b_, true);
+ }
+
+ /**
+ * Remove the given suffix and return true if the range ends with the given
+ * suffix; return false otherwise.
+ */
+ bool removeSuffix(const const_range_type& suffix) {
+ return endsWith(suffix) && (e_ -= suffix.size(), true);
+ }
+ bool removeSuffix(value_type suffix) {
+ return endsWith(suffix) && (--e_, true);
+ }
+
+ /**
+ * Replaces the content of the range, starting at position 'pos', with
+ * contents of 'replacement'. Entire 'replacement' must fit into the
+ * range. Returns false if 'replacements' does not fit. Example use:
+ *
+ * char in[] = "buffer";
+ * auto msp = MutablesStringPiece(input);
+ * EXPECT_TRUE(msp.replaceAt(2, "tt"));
+ * EXPECT_EQ(msp, "butter");
+ *
+ * // not enough space
+ * EXPECT_FALSE(msp.replace(msp.size() - 1, "rr"));
+ * EXPECT_EQ(msp, "butter"); // unchanged
+ */
+ bool replaceAt(size_t pos, const_range_type replacement) {
+ if (size() < pos + replacement.size()) {
+ return false;
+ }
+
+ std::copy(replacement.begin(), replacement.end(), begin() + pos);
+
+ return true;
+ }
+
+ /**
+ * Replaces all occurences of 'source' with 'dest'. Returns number
+ * of replacements made. Source and dest have to have the same
+ * length. Throws if the lengths are different. If 'source' is a
+ * pattern that is overlapping with itself, we perform sequential
+ * replacement: "aaaaaaa".replaceAll("aa", "ba") --> "bababaa"
+ *
+ * Example use:
+ *
+ * char in[] = "buffer";
+ * auto msp = MutablesStringPiece(input);
+ * EXPECT_EQ(msp.replaceAll("ff","tt"), 1);
+ * EXPECT_EQ(msp, "butter");
+ */
+ size_t replaceAll(const_range_type source, const_range_type dest) {
+ if (source.size() != dest.size()) {
+ throw std::invalid_argument(
+ "replacement must have the same size as source");
+ }
+
+ if (dest.empty()) {
+ return 0;
+ }
+
+ size_t pos = 0;
+ size_t num_replaced = 0;
+ size_type found = std::string::npos;
+ while ((found = find(source, pos)) != std::string::npos) {
+ replaceAt(found, dest);
+ pos += source.size();
+ ++num_replaced;
+ }
+
+ return num_replaced;
+ }
+
+ /**
+ * Splits this `Range` `[b, e)` in the position `i` dictated by the next
+ * occurence of `delimiter`.
+ *
+ * Returns a new `Range` `[b, i)` and adjusts this range to start right after
+ * the delimiter's position. This range will be empty if the delimiter is not
+ * found. If called on an empty `Range`, both this and the returned `Range`
+ * will be empty.
+ *
+ * Example:
+ *
+ * folly::StringPiece s("sample string for split_next");
+ * auto p = s.split_step(' ');
+ *
+ * // prints "string for split_next"
+ * cout << s << endl;
+ *
+ * // prints "sample"
+ * cout << p << endl;
+ *
+ * Example 2:
+ *
+ * void tokenize(StringPiece s, char delimiter) {
+ * while (!s.empty()) {
+ * cout << s.split_step(delimiter);
+ * }
+ * }
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ Range split_step(value_type delimiter) {
+ auto i = std::find(b_, e_, delimiter);
+ Range result(b_, i);
+
+ b_ = i == e_ ? e_ : std::next(i);
+
+ return result;
+ }
+
+ Range split_step(Range delimiter) {
+ auto i = find(delimiter);
+ Range result(b_, i == std::string::npos ? size() : i);
+
+ b_ = result.end() == e_ ? e_ : std::next(result.end(), delimiter.size());
+
+ return result;
+ }
+
+ /**
+ * Convenience method that calls `split_step()` and passes the result to a
+ * functor, returning whatever the functor does. Any additional arguments
+ * `args` passed to this function are perfectly forwarded to the functor.
+ *
+ * Say you have a functor with this signature:
+ *
+ * Foo fn(Range r) { }
+ *
+ * `split_step()`'s return type will be `Foo`. It works just like:
+ *
+ * auto result = fn(myRange.split_step(' '));
+ *
+ * A functor returning `void` is also supported.
+ *
+ * Example:
+ *
+ * void do_some_parsing(folly::StringPiece s) {
+ * auto version = s.split_step(' ', [&](folly::StringPiece x) {
+ * if (x.empty()) {
+ * throw std::invalid_argument("empty string");
+ * }
+ * return std::strtoull(x.begin(), x.end(), 16);
+ * });
+ *
+ * // ...
+ * }
+ *
+ * struct Foo {
+ * void parse(folly::StringPiece s) {
+ * s.split_step(' ', parse_field, bar, 10);
+ * s.split_step('\t', parse_field, baz, 20);
+ *
+ * auto const kludge = [](folly::StringPiece x, int &out, int def) {
+ * if (x == "null") {
+ * out = 0;
+ * } else {
+ * parse_field(x, out, def);
+ * }
+ * };
+ *
+ * s.split_step('\t', kludge, gaz);
+ * s.split_step(' ', kludge, foo);
+ * }
+ *
+ * private:
+ * int bar;
+ * int baz;
+ * int gaz;
+ * int foo;
+ *
+ * static parse_field(folly::StringPiece s, int &out, int def) {
+ * try {
+ * out = folly::to<int>(s);
+ * } catch (std::exception const &) {
+ * value = def;
+ * }
+ * }
+ * };
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+ template <typename TProcess, typename... Args>
+ auto split_step(value_type delimiter, TProcess &&process, Args &&...args)
+ -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...))
+ { return process(split_step(delimiter), std::forward<Args>(args)...); }
+
+ template <typename TProcess, typename... Args>
+ auto split_step(Range delimiter, TProcess &&process, Args &&...args)
+ -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...))
+ { return process(split_step(delimiter), std::forward<Args>(args)...); }
+