benchmark silo added
[c11concurrency-benchmarks.git] / silo / masstree / string.hh
diff --git a/silo/masstree/string.hh b/silo/masstree/string.hh
new file mode 100644 (file)
index 0000000..0fe9822
--- /dev/null
@@ -0,0 +1,880 @@
+/* Masstree
+ * Eddie Kohler, Yandong Mao, Robert Morris
+ * Copyright (c) 2012-2013 President and Fellows of Harvard College
+ * Copyright (c) 2012-2013 Massachusetts Institute of Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, subject to the conditions
+ * listed in the Masstree LICENSE file. These conditions include: you must
+ * preserve this copyright notice, and you cannot mention the copyright
+ * holders in advertising related to the Software without their permission.
+ * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
+ * notice is a summary of the Masstree LICENSE file; the license in that file
+ * is legally binding.
+ */
+#ifndef LCDF_STRING_HH
+#define LCDF_STRING_HH
+#include "string_base.hh"
+#include <string>
+#include <utility>
+namespace lcdf {
+
+class String : public String_base<String> {
+    struct memo_type;
+  public:
+    struct rep_type;
+
+    enum { max_length = 0x7FFFFFE0 };
+
+    typedef String substring_type;
+    typedef const String& argument_type;
+
+    inline String();
+    inline String(const String &x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline String(String &&x);
+#endif
+    template <typename T>
+    explicit inline String(const String_base<T>& str);
+    inline String(const char* cstr);
+    inline String(const std::string& str);
+    inline String(const char* s, int len);
+    inline String(const unsigned char* s, int len);
+    inline String(const char* first, const char* last);
+    inline String(const unsigned char* first, const unsigned char* last);
+    explicit inline String(bool x);
+    explicit inline String(char c);
+    explicit inline String(unsigned char c);
+    explicit String(int x);
+    explicit String(unsigned x);
+    explicit String(long x);
+    explicit String(unsigned long x);
+    explicit String(long long x);
+    explicit String(unsigned long long x);
+    explicit String(double x);
+    /** @cond never */
+    inline String(const rep_type& r);
+    /** @endcond never */
+    inline ~String();
+
+    static inline const String& make_empty();
+    static inline const String& make_out_of_memory();
+    static String make_uninitialized(int len);
+    static inline String make_stable(const char* cstr);
+    static inline String make_stable(const char* s, int len);
+    static inline String make_stable(const char* first, const char* last);
+    template <typename T>
+    static inline String make_stable(const String_base<T>& str);
+    static String make_fill(int c, int n);
+    static inline const String& make_zero();
+
+    inline const char* data() const;
+    inline int length() const;
+
+    inline const char *c_str() const;
+
+    inline String substring(const char* first, const char* last) const;
+    inline String substring(const unsigned char* first,
+                            const unsigned char* last) const;
+    inline String fast_substring(const char* first, const char* last) const;
+    inline String fast_substring(const unsigned char* first,
+                                 const unsigned char* last) const;
+    String substr(int pos, int len) const;
+    inline String substr(int pos) const;
+    String ltrim() const;
+    String rtrim() const;
+    String trim() const;
+
+    String lower() const;
+    String upper() const;
+    String printable(int type = 0) const;
+    String to_hex() const;
+
+    enum {
+        utf_strip_bom = 1,
+        utf_replacement = 2,
+        utf_prefer_le = 4
+    };
+
+    enum {
+        u_replacement = 0xFFFD
+    };
+
+    String windows1252_to_utf8() const;
+    String utf16be_to_utf8(int flags = 0) const;
+    String utf16le_to_utf8(int flags = 0) const;
+    String utf16_to_utf8(int flags = 0) const;
+    String cesu8_to_utf8(int flags = 0) const;
+    String utf8_to_utf8(int flags = 0) const;
+    String to_utf8(int flags = 0) const;
+
+    using String_base<String>::encode_json;
+    String encode_json() const;
+
+    using String_base<String>::encode_base64;
+    String encode_base64(bool pad = false) const;
+    using String_base<String>::decode_base64;
+    String decode_base64() const;
+
+    inline String& operator=(const String& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline String& operator=(String&& x);
+#endif
+    template <typename T>
+    inline String& operator=(const String_base<T>& str);
+    inline String& operator=(const char* cstr);
+    inline String& operator=(const std::string& str);
+
+    inline void assign(const String& x);
+#if HAVE_CXX_RVALUE_REFERENCES
+    inline void assign(String&& x);
+#endif
+    template <typename T>
+    inline void assign(const String_base<T>& str);
+    inline void assign(const char* cstr);
+    inline void assign(const std::string& str);
+    inline void assign(const char* first, const char* last);
+
+    inline void swap(String& x);
+
+    inline void append(const String& x);
+    inline void append(const char* cstr);
+    inline void append(const char* s, int len);
+    inline void append(const char* first, const char* last);
+    inline void append(const unsigned char* first, const unsigned char* last);
+    void append_fill(int c, int len);
+    char *append_uninitialized(int len);
+
+    inline String& operator+=(const String& x);
+    template <typename T>
+    inline String& operator+=(const String_base<T>& x);
+    inline String& operator+=(const char* cstr);
+    inline String& operator+=(char c);
+
+    // String operator+(String, const String &);
+    // String operator+(String, const char *);
+    // String operator+(const char *, const String &);
+
+    inline bool is_shared() const;
+    inline bool is_stable() const;
+
+    inline String unique() const;
+    inline void shrink_to_fit();
+
+    char* mutable_data();
+    inline unsigned char* mutable_udata();
+    char* mutable_c_str();
+
+    void align(int);
+
+    static const unsigned char* skip_utf8_char(const unsigned char* first,
+                                               const unsigned char* last);
+    static const char* skip_utf8_char(const char* first, const char* last);
+    static const unsigned char* skip_utf8_bom(const unsigned char* first,
+                                              const unsigned char* last);
+    static const char* skip_utf8_bom(const char* first, const char* last);
+
+    /** @cond never */
+    struct rep_type {
+        const char* data;
+        int length;
+        int memo_offset;
+
+        inline void ref() const {
+            if (memo_offset)
+                ++xmemo()->refcount;
+        }
+        inline void deref() const {
+            if (memo_offset && --xmemo()->refcount == 0)
+                String::delete_memo(xmemo());
+        }
+        inline void reset_ref() {
+            memo_offset = 0;
+        }
+
+      private:
+        inline memo_type* xmemo() const {
+            return reinterpret_cast<memo_type*>
+                (const_cast<char*>(data + memo_offset));
+        }
+        inline memo_type* memo() const {
+            return memo_offset ? xmemo() : nullptr;
+        }
+        inline void assign(const char* d, int l, memo_type* m) {
+            data = d;
+            length = l;
+            if (m) {
+                ++m->refcount;
+                memo_offset = static_cast<int>(reinterpret_cast<char*>(m) - d);
+            } else
+                memo_offset = 0;
+        }
+        inline void assign_noref(const char* d, int l, memo_type* m) {
+            data = d;
+            length = l;
+            if (m)
+                memo_offset = static_cast<int>(reinterpret_cast<char*>(m) - d);
+            else
+                memo_offset = 0;
+        }
+        friend class String;
+        friend class StringAccum;
+    };
+
+    const rep_type& internal_rep() const {
+        return _r;
+    }
+    void swap(rep_type& other_rep) {
+        using std::swap;
+        swap(_r, other_rep);
+    }
+    inline void assign(const rep_type& rep);
+    /** @endcond never */
+
+  private:
+    /** @cond never */
+    struct memo_type {
+        volatile uint32_t refcount;
+        uint32_t capacity;
+        volatile uint32_t dirty;
+#if HAVE_STRING_PROFILING > 1
+        memo_type** pprev;
+        memo_type* next;
+#endif
+        char real_data[8];      // but it might be more or less
+
+        inline void initialize(uint32_t capacity, uint32_t dirty);
+#if HAVE_STRING_PROFILING
+        void account_new();
+        void account_destroy();
+#else
+        inline void account_destroy() {}
+#endif
+    };
+
+    enum {
+        MEMO_SPACE = sizeof(memo_type) - 8 /* == sizeof(memo_type::real_data) */
+    };
+
+    struct null_memo {
+    };
+    /** @endcond never */
+
+    mutable rep_type _r;        // mutable for c_str()
+
+#if HAVE_STRING_PROFILING
+    static uint64_t live_memo_count;
+    static uint64_t memo_sizes[55];
+    static uint64_t live_memo_sizes[55];
+    static uint64_t live_memo_bytes[55];
+# if HAVE_STRING_PROFILING > 1
+    static memo_t *live_memos[55];
+# endif
+
+    static inline int profile_memo_size_bucket(uint32_t dirty, uint32_t capacity) {
+        if (capacity <= 16)
+            return dirty;
+        else if (capacity <= 32)
+            return 17 + (capacity - 17) / 2;
+        else if (capacity <= 64)
+            return 25 + (capacity - 33) / 8;
+        else
+            return 29 + 26 - ffs_msb(capacity - 1);
+    }
+
+    static void profile_update_memo_dirty(memo_type* memo, uint32_t old_dirty, uint32_t new_dirty, uint32_t capacity) {
+        if (capacity <= 16 && new_dirty != old_dirty) {
+            ++memo_sizes[new_dirty];
+            ++live_memo_sizes[new_dirty];
+            live_memo_bytes[new_dirty] += capacity;
+            --live_memo_sizes[old_dirty];
+            live_memo_bytes[old_dirty] -= capacity;
+# if HAVE_STRING_PROFILING > 1
+            if ((*memo->pprev = memo->next))
+                memo->next->pprev = memo->pprev;
+            memo->pprev = &live_memos[new_dirty];
+            if ((memo->next = *memo->pprev))
+                memo->next->pprev = &memo->next;
+            *memo->pprev = memo;
+# else
+            (void) memo;
+# endif
+        }
+    }
+
+    static void one_profile_report(StringAccum &sa, int i, int examples);
+#endif
+
+    inline String(const char* data, int length, memo_type* memo) {
+        _r.assign_noref(data, length, memo);
+    }
+    inline String(const char* data, int length, const null_memo&)
+        : _r{data, length, 0} {
+    }
+
+    inline void deref() const {
+        _r.deref();
+    }
+
+    void assign(const char* s, int len, bool need_deref);
+    void assign_out_of_memory();
+    void append(const char* s, int len, memo_type* memo);
+    static String hard_make_stable(const char *s, int len);
+    static inline memo_type* absent_memo() {
+        return reinterpret_cast<memo_type*>(uintptr_t(1));
+    }
+    static inline memo_type* create_memo(int capacity, int dirty);
+    static void delete_memo(memo_type* memo);
+    const char* hard_c_str() const;
+    bool hard_equals(const char* s, int len) const;
+
+    static const char int_data[20];
+    static const rep_type null_string_rep;
+    static const rep_type oom_string_rep;
+    static const rep_type zero_string_rep;
+
+    static int parse_cesu8_char(const unsigned char* s,
+                                const unsigned char* end);
+
+    friend struct rep_type;
+    friend class StringAccum;
+};
+
+
+/** @cond never */
+inline void String::memo_type::initialize(uint32_t capacity, uint32_t dirty) {
+    this->refcount = 1;
+    this->capacity = capacity;
+    this->dirty = dirty;
+#if HAVE_STRING_PROFILING
+    this->account_new();
+#endif
+}
+/** @endcond never */
+
+/** @brief Construct an empty String (with length 0). */
+inline String::String()
+    : _r{String_generic::empty_data, 0, 0} {
+}
+
+/** @brief Construct a copy of the String @a x. */
+inline String::String(const String& x)
+    : _r(x._r) {
+    _r.ref();
+}
+
+#if HAVE_CXX_RVALUE_REFERENCES
+/** @brief Move-construct a String from @a x. */
+inline String::String(String &&x)
+    : _r(x._r) {
+    x._r.reset_ref();
+}
+#endif
+
+/** @brief Construct a copy of the string @a str. */
+template <typename T>
+inline String::String(const String_base<T> &str) {
+    assign(str.data(), str.length(), false);
+}
+
+/** @brief Construct a String containing the C string @a cstr.
+    @param cstr a null-terminated C string
+    @return A String containing the characters of @a cstr, up to but not
+    including the terminating null character. */
+inline String::String(const char* cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr))
+        _r.assign(cstr, strlen(cstr), 0);
+    else
+        assign(cstr, -1, false);
+}
+
+/** @brief Construct a String containing the first @a len characters of
+    string @a s.
+    @param s a string
+    @param len number of characters to take from @a s.  If @a len @< 0,
+    then takes @c strlen(@a s) characters.
+    @return A String containing @a len characters of @a s. */
+inline String::String(const char* s, int len) {
+    assign(s, len, false);
+}
+
+/** @overload */
+inline String::String(const unsigned char* s, int len) {
+    assign(reinterpret_cast<const char*>(s), len, false);
+}
+
+/** @brief Construct a String containing the characters from @a first
+    to @a last.
+    @param first first character in string (begin iterator)
+    @param last pointer one past last character in string (end iterator)
+    @return A String containing the characters from @a first to @a last.
+
+    Constructs an empty string if @a first @>= @a last. */
+inline String::String(const char *first, const char *last) {
+    assign(first, (first < last ? last - first : 0), false);
+}
+
+/** @overload */
+inline String::String(const unsigned char* first, const unsigned char* last) {
+    assign(reinterpret_cast<const char*>(first),
+           (first < last ? last - first : 0), false);
+}
+
+/** @brief Construct a String from a std::string. */
+inline String::String(const std::string& str) {
+    assign(str.data(), str.length(), false);
+}
+
+/** @brief Construct a String equal to "true" or "false" depending on the
+    value of @a x. */
+inline String::String(bool x)
+    : _r{String_generic::bool_data + (-x & 6), 5 - x, 0} {
+    // bool_data equals "false\0true\0"
+}
+
+/** @brief Construct a String containing the single character @a c. */
+inline String::String(char c) {
+    assign(&c, 1, false);
+}
+
+/** @overload */
+inline String::String(unsigned char c) {
+    assign(reinterpret_cast<char*>(&c), 1, false);
+}
+
+inline String::String(const rep_type& r)
+    : _r(r) {
+    _r.ref();
+}
+
+/** @brief Destroy a String, freeing memory if necessary. */
+inline String::~String() {
+    deref();
+}
+
+/** @brief Return a const reference to an empty String.
+
+    May be quicker than String::String(). */
+inline const String& String::make_empty() {
+    return reinterpret_cast<const String &>(null_string_rep);
+}
+
+/** @brief Return a String containing @a len unknown characters. */
+inline String String::make_uninitialized(int len) {
+    String s;
+    s.append_uninitialized(len);
+    return s;
+}
+
+/** @brief Return a const reference to the string "0". */
+inline const String& String::make_zero() {
+    return reinterpret_cast<const String&>(zero_string_rep);
+}
+
+/** @brief Return a String that directly references the C string @a cstr.
+
+    The make_stable() functions are suitable for static constant strings
+    whose data is known to stay around forever, such as C string constants.
+
+    @warning The String implementation may access @a cstr's terminating null
+    character. */
+inline String String::make_stable(const char *cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr))
+        return String(cstr, strlen(cstr), null_memo());
+    else
+        return hard_make_stable(cstr, -1);
+}
+
+/** @brief Return a String that directly references the first @a len
+    characters of @a s.
+
+    If @a len @< 0, treats @a s as a null-terminated C string.
+
+    @warning The String implementation may access @a s[@a len], which
+    should remain constant even though it's not part of the String. */
+inline String String::make_stable(const char* s, int len) {
+    if (__builtin_constant_p(len) && len >= 0)
+        return String(s, len, null_memo());
+    else
+        return hard_make_stable(s, len);
+}
+
+/** @brief Return a String that directly references the character data in
+    [@a first, @a last).
+    @param first pointer to the first character in the character data
+    @param last pointer one beyond the last character in the character data
+    (but see the warning)
+
+    This function is suitable for static constant strings whose data is
+    known to stay around forever, such as C string constants.  Returns an
+    empty string if @a first @>= @a last.
+
+    @warning The String implementation may access *@a last, which should
+    remain constant even though it's not part of the String. */
+inline String String::make_stable(const char* first, const char* last) {
+    return String(first, (first < last ? last - first : 0), null_memo());
+}
+
+/** @overload */
+template <typename T>
+inline String String::make_stable(const String_base<T>& str) {
+    return String(str.data(), str.length(), null_memo());
+}
+
+/** @brief Return a pointer to the string's data.
+
+    Only the first length() characters are valid, and the string
+    might not be null-terminated. */
+inline const char* String::data() const {
+    return _r.data;
+}
+
+/** @brief Return the string's length. */
+inline int String::length() const {
+    return _r.length;
+}
+
+/** @brief Null-terminate the string.
+
+    The terminating null character isn't considered part of the string, so
+    this->length() doesn't change.  Returns a corresponding C string
+    pointer.  The returned pointer is semi-temporary; it will persist until
+    the string is destroyed or appended to. */
+inline const char* String::c_str() const {
+    // See also hard_c_str().
+#if HAVE_OPTIMIZE_SIZE || __OPTIMIZE_SIZE__
+    return hard_c_str();
+#else
+    // We may already have a '\0' in the right place.  If _memo has no
+    // capacity, then this is one of the special strings (null or
+    // stable). We are guaranteed, in these strings, that _data[_length]
+    // exists. Otherwise must check that _data[_length] exists.
+    const char* end_data = _r.data + _r.length;
+    memo_type* m = _r.memo();
+    if ((m && end_data >= m->real_data + m->dirty)
+        || *end_data != '\0') {
+        if (char *x = const_cast<String*>(this)->append_uninitialized(1)) {
+            *x = '\0';
+            --_r.length;
+        }
+    }
+    return _r.data;
+#endif
+}
+
+/** @brief Return a substring of the current string starting at @a first
+    and ending before @a last.
+    @param first pointer to the first substring character
+    @param last pointer one beyond the last substring character
+
+    Returns an empty string if @a first @>= @a last. Also returns an empty
+    string if @a first or @a last is out of range (i.e., either less than
+    this->begin() or greater than this->end()), but this should be
+    considered a programming error; a future version may generate a warning
+    for this case. */
+inline String String::substring(const char* first, const char* last) const {
+    if (first < last && first >= _r.data && last <= _r.data + _r.length) {
+        _r.ref();
+        return String(first, last - first, _r.memo());
+    } else
+        return String();
+}
+/** @overload */
+inline String String::substring(const unsigned char* first, const unsigned char* last) const {
+    return substring(reinterpret_cast<const char*>(first),
+                     reinterpret_cast<const char*>(last));
+}
+
+/** @brief Return a substring of the current string starting at @a first
+    and ending before @a last.
+    @param first pointer to the first substring character
+    @param last pointer one beyond the last substring character
+    @pre begin() <= @a first <= @a last <= end() */
+inline String String::fast_substring(const char* first, const char* last) const {
+    assert(begin() <= first && first <= last && last <= end());
+    _r.ref();
+    return String(first, last - first, _r.memo());
+}
+/** @overload */
+inline String String::fast_substring(const unsigned char* first, const unsigned char* last) const {
+    return fast_substring(reinterpret_cast<const char*>(first),
+                          reinterpret_cast<const char*>(last));
+}
+
+/** @brief Return the suffix of the current string starting at index @a pos.
+
+    If @a pos is negative, starts that far from the end of the string.
+    If @a pos is so negative that the suffix starts outside the string,
+    then the entire string is returned. If the substring is beyond the
+    end of the string (@a pos > length()), returns an empty string (but
+    this should be considered a programming error; a future version may
+    generate a warning for this case).
+
+    @note String::substr() is intended to behave like Perl's
+    substr(). */
+inline String String::substr(int pos) const {
+    return substr((pos <= -_r.length ? 0 : pos), _r.length);
+}
+
+inline void String::assign(const rep_type& rep) {
+    rep.ref();
+    _r.deref();
+    _r = rep;
+}
+
+/** @brief Assign this string to @a x. */
+inline void String::assign(const String& x) {
+    assign(x._r);
+}
+
+/** @brief Assign this string to @a x. */
+inline String& String::operator=(const String& x) {
+    assign(x);
+    return *this;
+}
+
+#if HAVE_CXX_RVALUE_REFERENCES
+/** @brief Move-assign this string to @a x. */
+inline void String::assign(String&& x) {
+    deref();
+    _r = x._r;
+    x._r.reset_ref();
+}
+
+/** @brief Move-assign this string to @a x. */
+inline String& String::operator=(String&& x) {
+    assign(std::move(x));
+    return *this;
+}
+#endif
+
+/** @brief Assign this string to the C string @a cstr. */
+inline void String::assign(const char* cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr)) {
+        deref();
+        _r.assign(cstr, strlen(cstr), 0);
+    } else
+        assign(cstr, -1, true);
+}
+
+/** @brief Assign this string to the C string @a cstr. */
+inline String& String::operator=(const char* cstr) {
+    assign(cstr);
+    return *this;
+}
+
+/** @brief Assign this string to the string @a str. */
+template <typename T>
+inline void String::assign(const String_base<T>& str) {
+    assign(str.data(), str.length(), true);
+}
+
+/** @brief Assign this string to the string @a str. */
+template <typename T>
+inline String& String::operator=(const String_base<T>& str) {
+    assign(str);
+    return *this;
+}
+
+/** @brief Assign this string to the std::string @a str. */
+inline void String::assign(const std::string& str) {
+    assign(str.data(), str.length(), true);
+}
+
+/** @brief Assign this string to the std::string @a str. */
+inline String& String::operator=(const std::string& str) {
+    assign(str);
+    return *this;
+}
+
+/** @brief Assign this string to string [@a first, @a last). */
+inline void String::assign(const char *first, const char *last) {
+    assign(first, last - first, true);
+}
+
+/** @brief Swap the values of this string and @a x. */
+inline void String::swap(String &x) {
+    using std::swap;
+    swap(_r, x._r);
+}
+
+/** @brief Append @a x to this string. */
+inline void String::append(const String& x) {
+    append(x.data(), x.length(), x._r.memo());
+}
+
+/** @brief Append the null-terminated C string @a cstr to this string.
+    @param cstr data to append */
+inline void String::append(const char* cstr) {
+    if (LCDF_CONSTANT_CSTR(cstr))
+        append(cstr, strlen(cstr), absent_memo());
+    else
+        append(cstr, -1, absent_memo());
+}
+
+/** @brief Append the first @a len characters of @a s to this string.
+    @param s data to append
+    @param len length of data
+
+    If @a len @< 0, treats @a s as a null-terminated C string. */
+inline void String::append(const char* s, int len) {
+    append(s, len, absent_memo());
+}
+
+/** @brief Appends the data from @a first to @a last to this string.
+
+    Does nothing if @a first @>= @a last. */
+inline void String::append(const char* first, const char* last) {
+    if (first < last)
+        append(first, last - first);
+}
+/** @overload */
+inline void String::append(const unsigned char* first,
+                           const unsigned char* last) {
+    if (first < last)
+        append(reinterpret_cast<const char*>(first), last - first);
+}
+
+/** @brief Append @a x to this string.
+    @return *this */
+inline String& String::operator+=(const String &x) {
+    append(x.data(), x.length(), x._r.memo());
+    return *this;
+}
+
+/** @brief Append the null-terminated C string @a cstr to this string.
+    @return *this */
+inline String& String::operator+=(const char* cstr) {
+    append(cstr);
+    return *this;
+}
+
+/** @brief Append the character @a c to this string.
+    @return *this */
+inline String &String::operator+=(char c) {
+    append(&c, 1);
+    return *this;
+}
+
+/** @brief Append the string @a x to this string.
+    @return *this */
+template <typename T>
+inline String &String::operator+=(const String_base<T> &x) {
+    append(x.data(), x.length());
+    return *this;
+}
+
+/** @brief Test if the String's data is shared or stable. */
+inline bool String::is_shared() const {
+    memo_type* m = _r.memo();
+    return !m || m->refcount != 1;
+}
+
+/** @brief Test if the String's data is stable. */
+inline bool String::is_stable() const {
+    return !_r.memo();
+}
+
+/** @brief Return a unique version of this String.
+
+    The return value shares no data with any other non-stable String. */
+inline String String::unique() const {
+    memo_type* m = _r.memo();
+    if (!m || m->refcount == 1)
+        return *this;
+    else
+        return String(_r.data, _r.data + _r.length);
+}
+
+/** @brief Reduce the memory allocation for this String.
+
+    After calling this function, this String shares no more than 256 bytes
+    of data with any other non-stable String. */
+inline void String::shrink_to_fit() {
+    memo_type* m = _r.memo();
+    if (m && m->refcount > 1 && (uint32_t) _r.length + 256 < m->capacity)
+        *this = String(_r.data, _r.data + _r.length);
+}
+
+/** @brief Return the unsigned char* version of mutable_data(). */
+inline unsigned char* String::mutable_udata() {
+    return reinterpret_cast<unsigned char*>(mutable_data());
+}
+
+/** @brief Return a const reference to a canonical out-of-memory String. */
+inline const String &String::make_out_of_memory() {
+    return reinterpret_cast<const String &>(oom_string_rep);
+}
+
+/** @brief Return a pointer to the next character in UTF-8 encoding.
+    @pre @a first @< @a last
+
+    If @a first doesn't point at a valid UTF-8 character, returns @a first. */
+inline const char* String::skip_utf8_char(const char* first, const char* last) {
+    return reinterpret_cast<const char*>(
+        skip_utf8_char(reinterpret_cast<const unsigned char*>(first),
+                       reinterpret_cast<const unsigned char*>(last)));
+}
+
+inline const unsigned char* String::skip_utf8_bom(const unsigned char* first,
+                                                  const unsigned char* last) {
+    if (last - first >= 3
+        && first[0] == 0xEF && first[1] == 0xBB && first[2] == 0xBF)
+        return first + 3;
+    else
+        return first;
+}
+
+inline const char* String::skip_utf8_bom(const char* first, const char* last) {
+    return reinterpret_cast<const char*>(
+        skip_utf8_bom(reinterpret_cast<const unsigned char*>(first),
+                      reinterpret_cast<const unsigned char*>(last)));
+}
+
+
+/** @relates String
+    @brief Concatenate the operands and return the result.
+
+    At most one of the two operands can be a null-terminated C string. */
+inline String operator+(String a, const String& b) {
+    a += b;
+    return a;
+}
+
+/** @relates String */
+inline String operator+(String a, const char* b) {
+    a.append(b);
+    return a;
+}
+
+/** @relates String */
+inline String operator+(const char* a, const String& b) {
+    String s1(a);
+    s1 += b;
+    return s1;
+}
+
+/** @relates String
+    @brief Concatenate the operands and return the result.
+
+    The second operand is a single character. */
+inline String operator+(String a, char b) {
+    a.append(&b, 1);
+    return a;
+}
+
+#if HAVE_CXX_USER_LITERALS
+inline String operator"" _S(const char* s, size_t len) {
+    return String::make_stable(s, s + len);
+}
+#endif
+
+inline void swap(String& a, String& b) {
+    a.swap(b);
+}
+
+} // namespace lcdf
+
+LCDF_MAKE_STRING_HASH(lcdf::String)
+#endif