From d16ca8941164259a312fac7dc01affcbb56e3af8 Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Fri, 13 Nov 2009 19:19:53 -0800 Subject: [PATCH] String representation: Don't store memo_ts at all for special strings. Cliff Frey discussion result. Don't know if it's faster, but it passes tests. --- include/click/string.hh | 26 ++++---- lib/string.cc | 136 +++++++++++++++++++--------------------- 2 files changed, 77 insertions(+), 85 deletions(-) diff --git a/include/click/string.hh b/include/click/string.hh index e329a0e066..8a8d7dcd38 100644 --- a/include/click/string.hh +++ b/include/click/string.hh @@ -39,7 +39,7 @@ class String { public: /** @brief Construct an empty String (with length 0). */ inline String() { - assign_memo(null_memo.real_data, 0, &null_memo); + assign_memo(&null_data, 0, 0); } /** @brief Construct a copy of the String @a x. */ @@ -86,7 +86,7 @@ class String { public: /** @brief Construct a String equal to "true" or "false" depending on the * value of @a x. */ explicit inline String(bool x) { - assign_memo(bool_data + (x ? 0 : 5), x ? 4 : 5, &permanent_memo); + assign_memo(bool_data + (x ? 0 : 5), x ? 4 : 5, 0); } /** @brief Construct a String containing the single character @a c. */ @@ -289,8 +289,7 @@ class String { public: // 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; - if ((_r.memo->capacity - && end_data >= _r.memo->real_data + _r.memo->dirty) + if ((_r.memo && end_data >= _r.memo->real_data + _r.memo->dirty) || *end_data != '\0') { if (char *x = const_cast(this)->append_garbage(1)) { *x = '\0'; @@ -585,7 +584,7 @@ class String { public: /** @brief Return true iff the String's data is shared or immutable. */ inline bool data_shared() const { - return !_r.memo->capacity || _r.memo->refcount != 1; + return !_r.memo || _r.memo->refcount != 1; } /** @brief Return a compact version of this String. @@ -593,7 +592,7 @@ class String { public: * The compact version shares no more than 256 bytes of data with any * other non-stable String. */ inline String compact() const { - if (!_r.memo->capacity || _r.memo->refcount == 1 + if (!_r.memo || _r.memo->refcount == 1 || (uint32_t) _r.length + 256 >= _r.memo->capacity) return *this; else @@ -612,7 +611,7 @@ class String { public: /** @brief Return true iff this is an out-of-memory string. */ inline bool out_of_memory() const { - return _r.data == oom_memo.real_data; + return _r.data == &oom_data; } /** @brief Return a const reference to an out-of-memory String. */ @@ -625,7 +624,7 @@ class String { public: * The returned value may be dereferenced; it points to a null * character. */ static inline const char *out_of_memory_data() { - return oom_memo.real_data; + return &oom_data; } @@ -706,8 +705,8 @@ class String { public: inline void assign_memo(const char *data, int length, memo_t *memo) const { _r.data = data; _r.length = length; - _r.memo = memo; - atomic_uint32_t::inc(memo->refcount); + if ((_r.memo = memo)) + atomic_uint32_t::inc(memo->refcount); } inline String(const char *data, int length, memo_t *memo) { @@ -719,7 +718,7 @@ class String { public: } inline void deref() const { - if (atomic_uint32_t::dec_and_test(_r.memo->refcount)) + if (_r.memo && atomic_uint32_t::dec_and_test(_r.memo->refcount)) delete_memo(_r.memo); } @@ -728,11 +727,10 @@ class String { public: static memo_t *create_memo(char *space, int dirty, int capacity); static void delete_memo(memo_t *memo); + static const char null_data; + static const char oom_data; static const char bool_data[11]; static const char int_data[20]; - static memo_t null_memo; - static memo_t permanent_memo; - static memo_t oom_memo; static const rep_t null_string_rep; static const rep_t oom_string_rep; diff --git a/lib/string.cc b/lib/string.cc index b4f298b53d..200cda878c 100644 --- a/lib/string.cc +++ b/lib/string.cc @@ -65,6 +65,8 @@ CLICK_DECLS * returns an out-of-memory string. */ +const char String::null_data = '\0'; +const char String::oom_data = '\0'; const char String::bool_data[] = "true\0false"; const char String::int_data[] = "0\0001\0002\0003\0004\0005\0006\0007\0008\0009"; @@ -74,21 +76,11 @@ const char String::int_data[] = "0\0001\0002\0003\0004\0005\0006\0007\0008\0009" # define MEMO_INITIALIZER_TAIL #endif -String::memo_t String::null_memo = { - 2, 0, 0 MEMO_INITIALIZER_TAIL, { '\0' } -}; -String::memo_t String::permanent_memo = { - 1, 0, 0 MEMO_INITIALIZER_TAIL, { '\0' } -}; -String::memo_t String::oom_memo = { - 2, 0, 0 MEMO_INITIALIZER_TAIL, { '\0' } -}; - const String::rep_t String::null_string_rep = { - null_memo.real_data, 0, &null_memo + &null_data, 0, 0 }; const String::rep_t String::oom_string_rep = { - oom_memo.real_data, 0, &oom_memo + &oom_data, 0, 0 }; #if HAVE_STRING_PROFILING @@ -206,7 +198,7 @@ String::profile_report(StringAccum &sa, int examples) String::String(int x) { if (x >= 0 && x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { char buf[128]; sprintf(buf, "%d", x); @@ -217,7 +209,7 @@ String::String(int x) String::String(unsigned x) { if (x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { char buf[128]; sprintf(buf, "%u", x); @@ -228,7 +220,7 @@ String::String(unsigned x) String::String(long x) { if (x >= 0 && x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { char buf[128]; sprintf(buf, "%ld", x); @@ -239,7 +231,7 @@ String::String(long x) String::String(unsigned long x) { if (x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { char buf[128]; sprintf(buf, "%lu", x); @@ -254,7 +246,7 @@ String::String(unsigned long x) String::String(long long x) { if (x >= 0 && x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { StringAccum sa; sa << x; @@ -265,7 +257,7 @@ String::String(long long x) String::String(unsigned long long x) { if (x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { StringAccum sa; sa << x; @@ -278,7 +270,7 @@ String::String(unsigned long long x) String::String(int64_t x) { if (x >= 0 && x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { StringAccum sa; sa << x; @@ -289,7 +281,7 @@ String::String(int64_t x) String::String(uint64_t x) { if (x < 10) - assign_memo(int_data + 2 * x, 1, &permanent_memo); + assign_memo(int_data + 2 * x, 1, 0); else { StringAccum sa; sa << x; @@ -320,7 +312,7 @@ String::make_stable(const char *s, int len) { if (len < 0) len = (s ? strlen(s) : 0); - return String(s, len, &permanent_memo); + return String(s, len, 0); } String @@ -350,66 +342,67 @@ String::make_numeric(uint_large_t num, int base, bool uppercase) void String::assign_out_of_memory() { - if (_r.memo) - deref(); - _r.memo = &oom_memo; - _r.data = _r.memo->real_data; - _r.length = 0; - atomic_uint32_t::inc(oom_memo.refcount); + if (_r.memo) + deref(); + _r.memo = 0; + _r.data = &oom_data; + _r.length = 0; } void String::assign(const char *str, int len, bool need_deref) { - if (!str) { - assert(len <= 0); - len = 0; - } else if (len < 0) - len = strlen(str); - - // need to start with dereference - if (need_deref) { - if (unlikely(str >= _r.memo->real_data - && str + len <= _r.memo->real_data + _r.memo->capacity)) { - // Be careful about "String s = ...; s = s.c_str();" - _r.data = str; - _r.length = len; - return; - } else - deref(); - } - - if (len == 0) { - _r.memo = (str == oom_memo.real_data ? &oom_memo : &null_memo); - atomic_uint32_t::inc(_r.memo->refcount); - - } else { - // Make the memo a multiple of 16 characters and bigger than 'len'. - int memo_capacity = (len + 15 + MEMO_SPACE) & ~15; - _r.memo = create_memo(0, len, memo_capacity - MEMO_SPACE); - if (!_r.memo) { - assign_out_of_memory(); - return; + if (!str) { + assert(len <= 0); + len = 0; + } else if (len < 0) + len = strlen(str); + + // need to start with dereference + if (need_deref) { + if (unlikely(_r.memo + && str >= _r.memo->real_data + && str + len <= _r.memo->real_data + _r.memo->capacity)) { + // Be careful about "String s = ...; s = s.c_str();" + _r.data = str; + _r.length = len; + return; + } else + deref(); + } + + if (len == 0) { + _r.memo = 0; + _r.data = (str == &oom_data ? str : &null_data); + + } else { + // Make the memo a multiple of 16 characters and bigger than 'len'. + int memo_capacity = (len + 15 + MEMO_SPACE) & ~15; + _r.memo = create_memo(0, len, memo_capacity - MEMO_SPACE); + if (!_r.memo) { + assign_out_of_memory(); + return; + } + memcpy(_r.memo->real_data, str, len); + _r.data = _r.memo->real_data; } - memcpy(_r.memo->real_data, str, len); - } - _r.data = _r.memo->real_data; - _r.length = len; + _r.length = len; } char * String::append_garbage(int len) { // Appending anything to "out of memory" leaves it as "out of memory" - if (len <= 0 || _r.memo == &oom_memo) + if (len <= 0 || _r.data == &oom_data) return 0; // If we can, append into unused space. First, we check that there's // enough unused space for 'len' characters to fit; then, we check // that the unused space immediately follows the data in '*this'. - uint32_t dirty = _r.memo->dirty; - if (_r.memo->capacity > dirty + len) { + uint32_t dirty; + if (_r.memo + && ((dirty = _r.memo->dirty), _r.memo->capacity > dirty + len)) { char *real_dirty = _r.memo->real_data + dirty; if (real_dirty == _r.data + _r.length && atomic_uint32_t::compare_and_swap(_r.memo->dirty, dirty, dirty + len)) { @@ -467,13 +460,14 @@ String::append(const char *s, int len) } else if (len < 0) len = strlen(s); - if (s == oom_memo.real_data) + if (s == &oom_data) // Appending "out of memory" to a regular string makes it "out of // memory" assign_out_of_memory(); else if (unlikely(len == 0)) /* do nothing */; - else if (likely(!(s >= _r.memo->real_data + else if (likely(!(_r.memo + && s >= _r.memo->real_data && s + len <= _r.memo->real_data + _r.memo->capacity))) { if (char *space = append_garbage(len)) memcpy(space, s, len); @@ -497,12 +491,12 @@ String::mutable_data() { // If _memo has a capacity (it's not one of the special strings) and it's // uniquely referenced, return _data right away. - if (_r.memo->capacity && _r.memo->refcount == 1) + if (_r.memo && _r.memo->refcount == 1) return const_cast(_r.data); // Otherwise, make a copy of it. Rely on: deref() doesn't change _data or // _length; and if _capacity == 0, then deref() doesn't free _real_data. - assert(!_r.memo->capacity || _r.memo->refcount > 1); + assert(!_r.memo || _r.memo->refcount > 1); deref(); assign(_r.data, _r.length, false); return const_cast(_r.data); @@ -794,7 +788,7 @@ String::equals(const char *s, int len) const else if (_r.data == s) return true; else if (len == 0) - return (s != oom_memo.real_data && _r.memo != &oom_memo); + return (s != &oom_data && _r.data != &oom_data); else return memcmp(_r.data, s, len) == 0; } @@ -810,7 +804,7 @@ String::starts_with(const char *s, int len) const else if (_r.data == s) return true; else if (len == 0) - return (s != oom_memo.real_data && _r.memo != &oom_memo); + return (s != &oom_data && _r.data != &oom_data); else return memcmp(_r.data, s, len) == 0; } @@ -822,9 +816,9 @@ String::compare(const char *s, int len) const len = strlen(s); if (_r.data == s) return _r.length - len; - else if (_r.memo == &oom_memo) + else if (_r.data == &oom_data) return 1; - else if (s == oom_memo.real_data) + else if (s == &oom_data) return -1; else if (_r.length == len) return memcmp(_r.data, s, len);