diff options
Diffstat (limited to 'Build/source/texk/lcdf-typetools/include/lcdf/string.hh')
-rw-r--r-- | Build/source/texk/lcdf-typetools/include/lcdf/string.hh | 150 |
1 files changed, 112 insertions, 38 deletions
diff --git a/Build/source/texk/lcdf-typetools/include/lcdf/string.hh b/Build/source/texk/lcdf-typetools/include/lcdf/string.hh index 00883a7265f..a6950833cf1 100644 --- a/Build/source/texk/lcdf-typetools/include/lcdf/string.hh +++ b/Build/source/texk/lcdf-typetools/include/lcdf/string.hh @@ -12,10 +12,7 @@ class String { public: /** @brief Construct an empty String (with length 0). */ inline String() { - _r.data = null_memo.real_data; - _r.length = 0; - _r.memo = &null_memo; - ++_r.memo->refcount; + assign_memo(&null_data, 0, 0); } /** @brief Construct a copy of the String @a x. */ @@ -60,12 +57,9 @@ class String { public: } /** @brief Construct a String equal to "true" or "false" depending on the - * value of @a b. */ - explicit inline String(bool b) { - _r.data = bool_data + (b ? 0 : 5); - _r.length = (b ? 4 : 5); - _r.memo = &permanent_memo; - ++_r.memo->refcount; + * value of @a x. */ + explicit inline String(bool x) { + assign_memo(bool_data + (x ? 0 : 5), x ? 4 : 5, 0); } /** @brief Construct a String containing the single character @a c. */ @@ -89,8 +83,8 @@ class String { public: /** @overload */ explicit String(double x); #if HAVE_PERMSTRING - inline String(PermString p) { - assign(p); + inline String(PermString x) { + assign(x); } #endif @@ -115,17 +109,24 @@ class String { public: * * This function is suitable for static constant strings whose data is * known to stay around forever, such as C string constants. If @a len @< - * 0, treats @a s as a null-terminated C string. */ + * 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. */ static String make_stable(const char *s, int len = -1); /** @brief Return a String that directly references the character data in * [@a begin, @a end). * @param begin pointer to the first character in the character data * @param end 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 begin @>= @a end. */ + * empty string if @a begin @>= @a end. + * + * @warning The String implementation may access *@a end, which should + * remain constant even though it's not part of the String. */ static inline String make_stable(const char *begin, const char *end) { if (begin < end) return String::make_stable(begin, end - begin); @@ -226,7 +227,21 @@ class String { public: * 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. */ - const char *c_str() const; + inline const char *c_str() const { + // We may already have a '\0' in the right place. If _memo does not + // exist, then this is one of the special strings (null or + // stable). We are guaranteed, in these strings, that _data[_length] + // exists. Otherwise must check whether _data[_length] exists. + const char *end_data = _r.data + _r.length; + if ((_r.memo && end_data >= _r.memo->real_data + _r.memo->dirty) + || *end_data != '\0') { + if (char *x = const_cast<String *>(this)->append_garbage(1)) { + *x = '\0'; + --_r.length; + } + } + return _r.data; + } #if HAVE_PERMSTRING operator PermString() const { @@ -525,7 +540,19 @@ 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. + * + * The compact version shares no more than 256 bytes of data with any + * other non-stable String. */ + inline String compact() const { + if (!_r.memo || _r.memo->refcount == 1 + || (uint32_t) _r.length + 256 >= _r.memo->capacity) + return *this; + else + return String(_r.data, _r.data + _r.length); } /** @brief Ensure the string's data is unshared and return a mutable @@ -546,7 +573,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_string_data; + return _r.data == &oom_data; } /** @brief Return a const reference to an out-of-memory String. */ @@ -559,7 +586,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_string_data; + return &oom_data; } private: @@ -569,7 +596,15 @@ class String { public: volatile uint32_t refcount; uint32_t capacity; volatile uint32_t dirty; - char *real_data; +#if HAVE_STRING_PROFILING > 1 + memo_t **pprev; + memo_t *next; +#endif + char real_data[1]; // but it is almost certainly more + }; + + enum { + MEMO_SPACE = offsetof(memo_t, real_data) }; struct rep_t { @@ -579,46 +614,85 @@ class String { public: }; /** @endcond never */ - mutable rep_t _r; // mutable for c_str() + mutable rep_t _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_t *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 + } + } - inline String(const char *data, int length, memo_t *memo) { + static void one_profile_report(StringAccum &sa, int i, int examples); +#endif + + inline void assign_memo(const char *data, int length, memo_t *memo) const { _r.data = data; _r.length = length; - _r.memo = memo; - ++memo->refcount; + if ((_r.memo = memo)) + ++memo->refcount; + } + + inline String(const char *data, int length, memo_t *memo) { + assign_memo(data, length, memo); } inline void assign(const String &x) const { - _r.data = x._r.data; - _r.length = x._r.length; - _r.memo = x._r.memo; - ++_r.memo->refcount; + assign_memo(x._r.data, x._r.length, x._r.memo); } inline void deref() const { - if (--_r.memo->refcount == 0) + if (_r.memo && --_r.memo->refcount == 0) delete_memo(_r.memo); } void assign(const char *cstr, int len, bool need_deref); #if HAVE_PERMSTRING inline void assign(PermString x) const { - _r.data = x.c_str(); - _r.length = x.length(); - _r.memo = &permanent_memo; - ++_r.memo->refcount; + assign_memo(x.c_str(), x.length(), 0); } #endif void assign_out_of_memory(); - static memo_t *create_memo(char *data, int dirty, int capacity); + static memo_t *create_memo(char *space, int dirty, int capacity); static void delete_memo(memo_t *memo); - static const char null_string_data; - static const char oom_string_data; + static const char null_data; + static const char oom_data; static const char bool_data[11]; - static memo_t null_memo; - static memo_t permanent_memo; - static memo_t oom_memo; + static const char int_data[20]; static const rep_t null_string_rep; static const rep_t oom_string_rep; |