1 // -*- related-file-name: "../include/lcdf/straccum.hh" -*-
2 /*
3  * straccum.{cc,hh} -- build up strings with operator<<
4  * Eddie Kohler
5  *
6  * Copyright (c) 1999-2000 Massachusetts Institute of Technology
7  * Copyright (c) 2001-2019 Eddie Kohler
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, subject to the conditions
12  * listed in the Click LICENSE file. These conditions include: you must
13  * preserve this copyright notice, and you cannot mention the copyright
14  * holders in advertising related to the Software without their permission.
15  * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
16  * notice is a summary of the Click LICENSE file; the license in that file is
17  * legally binding.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 #include <lcdf/straccum.hh>
24 #include <lcdf/vector.hh>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <errno.h>
29 
30 /** @class StringAccum
31  * @brief Efficiently build up Strings from pieces.
32  *
33  * Like the String class, StringAccum represents a string of characters.
34  * However, unlike a String, a StringAccum is inherently mutable, and
35  * efficiently supports building up a large string from many smaller pieces.
36  *
37  * StringAccum objects support operator<<() operations for most fundamental
38  * data types.  A StringAccum is generally built up by operator<<(), and then
39  * turned into a String by the take_string() method.  Extracting the String
40  * from a StringAccum does no memory allocation or copying; the StringAccum's
41  * memory is donated to the String.
42  *
43  * <h3>Out-of-memory StringAccums</h3>
44  *
45  * When there is not enough memory to add requested characters to a
46  * StringAccum object, the object becomes a special "out-of-memory"
47  * StringAccum.  Out-of-memory objects are contagious: the result of any
48  * concatenation operation involving an out-of-memory StringAccum is another
49  * out-of-memory StringAccum.  Appending an out-of-memory String with "sa <<
50  * s" makes "sa" out-of-memory.  (However, other usually-equivalent calls,
51  * such as "sa.append(s.begin(), s.end())", <em>do not</em> make "sa"
52  * out-of-memory.)  Calling take_string() on an out-of-memory StringAccum
53  * returns an out-of-memory String.
54  *
55  * Out-of-memory StringAccum objects have length zero.
56  */
57 
58 
59 void
assign_out_of_memory()60 StringAccum::assign_out_of_memory()
61 {
62     assert(_cap >= 0);
63     if (_cap > 0)
64         delete[] (_s - MEMO_SPACE);
65     _s = reinterpret_cast<unsigned char *>(const_cast<char *>(String::out_of_memory_data()));
66     _cap = -1;
67     _len = 0;
68 }
69 
70 char *
grow(int want)71 StringAccum::grow(int want)
72 {
73     // can't append to out-of-memory strings
74     if (_cap < 0) {
75         errno = ENOMEM;
76         return 0;
77     }
78 
79     int ncap = (_cap ? (_cap + MEMO_SPACE) * 2 : 128) - MEMO_SPACE;
80     while (ncap <= want)
81         ncap = (ncap + MEMO_SPACE) * 2 - MEMO_SPACE;
82 
83     unsigned char *n = new unsigned char[ncap + MEMO_SPACE];
84     if (!n) {
85         assign_out_of_memory();
86         errno = ENOMEM;
87         return 0;
88     }
89     n += MEMO_SPACE;
90 
91     if (_s) {
92         memcpy(n, _s, _len);
93         delete[] (_s - MEMO_SPACE);
94     }
95     _s = n;
96     _cap = ncap;
97     return reinterpret_cast<char *>(_s + _len);
98 }
99 
100 int
resize(int len)101 StringAccum::resize(int len)
102 {
103     assert(len >= 0);
104     if (len > _cap && !grow(len))
105         return -ENOMEM;
106     else {
107         _len = len;
108         return 0;
109     }
110 }
111 
112 char *
hard_extend(int nadjust,int nreserve)113 StringAccum::hard_extend(int nadjust, int nreserve)
114 {
115     char *x = grow(_len + nadjust + nreserve);
116     if (x)
117         _len += nadjust;
118     return x;
119 }
120 
121 const char *
c_str()122 StringAccum::c_str()
123 {
124     if (_len < _cap || grow(_len))
125         _s[_len] = '\0';
126     return reinterpret_cast<char *>(_s);
127 }
128 
129 void
append_fill(int c,int len)130 StringAccum::append_fill(int c, int len)
131 {
132     if (char *s = extend(len))
133         memset(s, c, len);
134 }
135 
136 void
append_utf8_hard(unsigned ch)137 StringAccum::append_utf8_hard(unsigned ch)
138 {
139     if (ch < 0x80)
140         append((unsigned char) ch);
141     else if (ch < 0x800) {
142         append((unsigned char) (0xC0 + ((ch >> 6) & 0x1F)));
143         append((unsigned char) (0x80 + (ch & 0x3F)));
144     } else if (ch < 0x10000) {
145         append((unsigned char) (0xE0 + ((ch >> 12) & 0x0F)));
146         append((unsigned char) (0x80 + ((ch >> 6) & 0x3F)));
147         append((unsigned char) (0x80 + (ch & 0x3F)));
148     } else if (ch < 0x110000) {
149         append((unsigned char) (0xF0 + ((ch >> 18) & 0x07)));
150         append((unsigned char) (0x80 + ((ch >> 12) & 0x3F)));
151         append((unsigned char) (0x80 + ((ch >> 6) & 0x3F)));
152         append((unsigned char) (0x80 + (ch & 0x3F)));
153     } else
154         append((unsigned char) '?');
155 }
156 
157 void
hard_append(const char * s,int len)158 StringAccum::hard_append(const char *s, int len)
159 {
160     // We must be careful about calls like "sa.append(sa.begin(), sa.end())";
161     // a naive implementation might use sa's data after freeing it.
162     const char *my_s = reinterpret_cast<char *>(_s);
163 
164     if (len <= 0) {
165         // do nothing
166     } else if (_len + len <= _cap) {
167     success:
168         memcpy(_s + _len, s, len);
169         _len += len;
170     } else if (s < my_s || s >= my_s + _cap) {
171         if (grow(_len + len))
172             goto success;
173     } else {
174         unsigned char *old_s = _s;
175         int old_len = _len;
176 
177         _s = 0;
178         _len = 0;
179         _cap = 0;
180 
181         if (char *new_s = extend(old_len + len)) {
182             memcpy(new_s, old_s, old_len);
183             memcpy(new_s + old_len, s, len);
184         }
185 
186         delete[] (old_s - MEMO_SPACE);
187     }
188 }
189 
190 void
append(const char * s)191 StringAccum::append(const char *s)
192 {
193     hard_append(s, strlen(s));
194 }
195 
196 String
take_string()197 StringAccum::take_string()
198 {
199     int len = length();
200     int cap = _cap;
201     char *str = reinterpret_cast<char *>(_s);
202     if (len > 0) {
203         _s = 0;
204         _len = _cap = 0;
205         return String::make_claim(str, len, cap);
206     } else if (!out_of_memory())
207         return String();
208     else {
209         clear();
210         return String::make_out_of_memory();
211     }
212 }
213 
214 void
swap(StringAccum & o)215 StringAccum::swap(StringAccum &o)
216 {
217     unsigned char *os = o._s;
218     int olen = o._len, ocap = o._cap;
219     o._s = _s;
220     o._len = _len, o._cap = _cap;
221     _s = os;
222     _len = olen, _cap = ocap;
223 }
224 
225 /** @relates StringAccum
226     @brief Append decimal representation of @a i to @a sa.
227     @return @a sa */
228 StringAccum &
operator <<(StringAccum & sa,long i)229 operator<<(StringAccum &sa, long i)
230 {
231     if (char *x = sa.reserve(24)) {
232         int len = sprintf(x, "%ld", i);
233         sa.adjust_length(len);
234     }
235     return sa;
236 }
237 
238 /** @relates StringAccum
239     @brief Append decimal representation of @a u to @a sa.
240     @return @a sa */
241 StringAccum &
operator <<(StringAccum & sa,unsigned long u)242 operator<<(StringAccum &sa, unsigned long u)
243 {
244     if (char *x = sa.reserve(24)) {
245         int len = sprintf(x, "%lu", u);
246         sa.adjust_length(len);
247     }
248     return sa;
249 }
250 
251 StringAccum &
operator <<(StringAccum & sa,double d)252 operator<<(StringAccum &sa, double d)
253 {
254     if (char *x = sa.reserve(256)) {
255         int len = sprintf(x, "%.12g", d);
256         sa.adjust_length(len);
257     }
258     return sa;
259 }
260 
261 StringAccum &
snprintf(int n,const char * format,...)262 StringAccum::snprintf(int n, const char *format, ...)
263 {
264     va_list val;
265     va_start(val, format);
266     if (char *x = reserve(n + 1)) {
267 #if HAVE_VSNPRINTF
268         int len = vsnprintf(x, n + 1, format, val);
269 #else
270         int len = vsprintf(x, format, val);
271         assert(len <= n);
272 #endif
273         adjust_length(len);
274     }
275     va_end(val);
276     return *this;
277 }
278 
279 void
append_break_lines(const String & text,int linelen,const String & leftmargin)280 StringAccum::append_break_lines(const String& text, int linelen, const String &leftmargin)
281 {
282     if (text.length() == 0)
283         return;
284     const char* line = text.begin();
285     const char* ends = text.end();
286     linelen -= leftmargin.length();
287     for (const char* s = line; s < ends; s++) {
288         const char* start = s;
289         while (s < ends && isspace((unsigned char) *s))
290             s++;
291         const char* word = s;
292         while (s < ends && !isspace((unsigned char) *s))
293             s++;
294         if (s - line > linelen && start > line) {
295             *this << leftmargin;
296             append(line, start - line);
297             *this << '\n';
298             line = word;
299         }
300     }
301     if (line < text.end()) {
302         *this << leftmargin;
303         append(line, text.end() - line);
304         *this << '\n';
305     }
306 }
307