summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/include/lcdf/string.hh
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/include/lcdf/string.hh')
-rw-r--r--Build/source/texk/lcdf-typetools/include/lcdf/string.hh150
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;