forked from kohler/masstree-beta
-
Notifications
You must be signed in to change notification settings - Fork 1
/
straccum.cc
398 lines (367 loc) · 11.7 KB
/
straccum.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
/* 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.
*/
/*
* straccum.{cc,hh} -- build up strings with operator<<
* Eddie Kohler
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2001-2013 Eddie Kohler
*
* 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 Click 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 Click LICENSE file; the license in that file
* is legally binding.
*/
#include "straccum.hh"
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
namespace lcdf {
/** @class StringAccum
* @brief Efficiently build up Strings from pieces.
*
* Like the String class, StringAccum represents a string of characters.
* However, unlike a String, a StringAccum is inherently mutable, and
* efficiently supports building up a large string from many smaller pieces.
*
* StringAccum objects support operator<<() operations for most fundamental
* data types. A StringAccum is generally built up by operator<<(), and then
* turned into a String by the take_string() method. Extracting the String
* from a StringAccum does no memory allocation or copying; the StringAccum's
* memory is donated to the String.
*
* <h3>Out-of-memory StringAccums</h3>
*
* When there is not enough memory to add requested characters to a
* StringAccum object, the object becomes a special "out-of-memory"
* StringAccum. Out-of-memory objects are contagious: the result of any
* concatenation operation involving an out-of-memory StringAccum is another
* out-of-memory StringAccum. Calling take_string() on an out-of-memory
* StringAccum returns an out-of-memory String.
*
* Note that appending an out-of-memory String to a StringAccum <em>does
* not</em> make the StringAccum out-of-memory.
*/
/** @brief Change this StringAccum into an out-of-memory StringAccum. */
void
StringAccum::assign_out_of_memory()
{
if (r_.cap > 0)
delete[] reinterpret_cast<char*>(r_.s - memo_space);
r_.s = reinterpret_cast<unsigned char*>(const_cast<char*>(String_generic::empty_data));
r_.cap = -1;
r_.len = 0;
}
char* StringAccum::grow(int ncap) {
// can't append to out-of-memory strings
if (r_.cap < 0) {
errno = ENOMEM;
return 0;
}
if (ncap < r_.cap)
return reinterpret_cast<char*>(r_.s + r_.len);
else if (ncap < 128)
ncap = 128;
else if (r_.cap < (1 << 20) && ncap < (r_.cap + memo_space) * 2 - memo_space)
ncap = (r_.cap + memo_space) * 2 - memo_space;
else if (r_.cap >= (1 << 20) && ncap < r_.cap + (1 << 19))
ncap = r_.cap + (1 << 19);
char* n = new char[ncap + memo_space];
if (!n) {
assign_out_of_memory();
errno = ENOMEM;
return 0;
}
n += memo_space;
if (r_.cap > 0) {
memcpy(n, r_.s, r_.len);
delete[] reinterpret_cast<char*>(r_.s - memo_space);
}
r_.s = reinterpret_cast<unsigned char*>(n);
r_.cap = ncap;
return reinterpret_cast<char*>(r_.s + r_.len);
}
/** @brief Set the StringAccum's length to @a len.
@pre @a len >= 0
@return 0 on success, -ENOMEM on failure */
int
StringAccum::resize(int len)
{
assert(len >= 0);
if (len > r_.cap && !grow(len))
return -ENOMEM;
else {
r_.len = len;
return 0;
}
}
char *
StringAccum::hard_extend(int nadjust, int nreserve)
{
char *x;
if (r_.len + nadjust + nreserve <= r_.cap)
x = reinterpret_cast<char*>(r_.s + r_.len);
else
x = grow(r_.len + nadjust + nreserve);
if (x)
r_.len += nadjust;
return x;
}
void StringAccum::transfer_from(String& x) {
if (x.is_shared() || x._r.memo_offset != -memo_space) {
append(x.begin(), x.end());
x._r.deref();
} else {
r_.s = const_cast<unsigned char*>(x.udata());
r_.len = x.length();
r_.cap = x._r.memo()->capacity;
}
}
/** @brief Null-terminate this StringAccum and return its data.
Note that the null character does not contribute to the StringAccum's
length(), and later append() and similar operations can overwrite it. If
appending the null character fails, the StringAccum becomes
out-of-memory and the returned value is a null string. */
const char *
StringAccum::c_str()
{
if (r_.len < r_.cap || grow(r_.len))
r_.s[r_.len] = '\0';
return reinterpret_cast<char *>(r_.s);
}
/** @brief Append @a len copies of character @a c to the StringAccum. */
void
StringAccum::append_fill(int c, int len)
{
if (char *s = extend(len))
memset(s, c, len);
}
void
StringAccum::hard_append(const char *s, int len)
{
// We must be careful about calls like "sa.append(sa.begin(), sa.end())";
// a naive implementation might use sa's data after freeing it.
const char *my_s = reinterpret_cast<char *>(r_.s);
if (r_.len + len <= r_.cap) {
success:
memcpy(r_.s + r_.len, s, len);
r_.len += len;
} else if (likely(s < my_s || s >= my_s + r_.cap)) {
if (grow(r_.len + len))
goto success;
} else {
rep_t old_r = r_;
r_ = rep_t();
if (char *new_s = extend(old_r.len + len)) {
memcpy(new_s, old_r.s, old_r.len);
memcpy(new_s + old_r.len, s, len);
}
delete[] reinterpret_cast<char*>(old_r.s - memo_space);
}
}
void
StringAccum::hard_append_cstr(const char *cstr)
{
append(cstr, strlen(cstr));
}
bool
StringAccum::append_utf8_hard(int ch)
{
if (ch < 0x8000) {
append(static_cast<char>(0xC0 | (ch >> 6)));
goto char1;
} else if (ch < 0x10000) {
if (unlikely((ch >= 0xD800 && ch < 0xE000) || ch > 0xFFFD))
return false;
append(static_cast<char>(0xE0 | (ch >> 12)));
goto char2;
} else if (ch < 0x110000) {
append(static_cast<char>(0xF0 | (ch >> 18)));
append(static_cast<char>(0x80 | ((ch >> 12) & 0x3F)));
char2:
append(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
char1:
append(static_cast<char>(0x80 | (ch & 0x3F)));
} else
return false;
return true;
}
/** @brief Return a String object with this StringAccum's contents.
This operation donates the StringAccum's memory to the returned String.
After a call to take_string(), the StringAccum object becomes empty, and
any future append() operations may cause memory allocations. If the
StringAccum is out-of-memory, the returned String is also out-of-memory,
but the StringAccum's out-of-memory state is reset. */
String
StringAccum::take_string()
{
int len = length();
int cap = r_.cap;
char* str = reinterpret_cast<char*>(r_.s);
if (len > 0 && cap > 0) {
String::memo_type* memo =
reinterpret_cast<String::memo_type*>(r_.s - memo_space);
memo->initialize(cap, len);
r_ = rep_t();
return String(str, len, memo);
} else if (!out_of_memory())
return String();
else {
clear();
return String::make_out_of_memory();
}
}
/** @brief Swap this StringAccum's contents with @a x. */
void
StringAccum::swap(StringAccum &x)
{
rep_t xr = x.r_;
x.r_ = r_;
r_ = xr;
}
/** @relates StringAccum
@brief Append decimal representation of @a i to @a sa.
@return @a sa */
StringAccum &
operator<<(StringAccum &sa, long i)
{
if (char *x = sa.reserve(24)) {
int len = sprintf(x, "%ld", i);
sa.adjust_length(len);
}
return sa;
}
/** @relates StringAccum
@brief Append decimal representation of @a u to @a sa.
@return @a sa */
StringAccum &
operator<<(StringAccum &sa, unsigned long u)
{
if (char *x = sa.reserve(24)) {
int len = sprintf(x, "%lu", u);
sa.adjust_length(len);
}
return sa;
}
/** @relates StringAccum
@brief Append decimal representation of @a i to @a sa.
@return @a sa */
StringAccum &
operator<<(StringAccum &sa, long long i)
{
if (char *x = sa.reserve(24)) {
int len = sprintf(x, "%lld", i);
sa.adjust_length(len);
}
return sa;
}
/** @relates StringAccum
@brief Append decimal representation of @a u to @a sa.
@return @a sa */
StringAccum &
operator<<(StringAccum &sa, unsigned long long u)
{
if (char *x = sa.reserve(24)) {
int len = sprintf(x, "%llu", u);
sa.adjust_length(len);
}
return sa;
}
StringAccum &
operator<<(StringAccum &sa, double d)
{
if (char *x = sa.reserve(256)) {
int len = sprintf(x, "%.12g", d);
sa.adjust_length(len);
}
return sa;
}
/** @brief Append result of vsnprintf() to this StringAccum.
@param n maximum number of characters to print
@param format format argument to snprintf()
@param val argument set
@return *this
@sa snprintf */
StringAccum &
StringAccum::vsnprintf(int n, const char *format, va_list val)
{
if (char *x = reserve(n + 1)) {
#if HAVE_VSNPRINTF
int len = ::vsnprintf(x, n + 1, format, val);
#else
int len = vsprintf(x, format, val);
assert(len <= n);
#endif
adjust_length(len);
}
return *this;
}
/** @brief Append result of snprintf() to this StringAccum.
@param n maximum number of characters to print
@param format format argument to snprintf()
@return *this
The terminating null character is not appended to the string.
@note The safe vsnprintf() variant is called if it exists. It does in
the Linux kernel, and on modern Unix variants. However, if it does not
exist on your machine, then this function is actually unsafe, and you
should make sure that the printf() invocation represented by your
arguments will never write more than @a n characters, not including the
terminating null.
@sa vsnprintf */
StringAccum &
StringAccum::snprintf(int n, const char *format, ...)
{
va_list val;
va_start(val, format);
vsnprintf(n, format, val);
va_end(val);
return *this;
}
void
StringAccum::append_break_lines(const String& text, int linelen, const String &leftmargin)
{
if (text.length() == 0)
return;
const char* line = text.begin();
const char* ends = text.end();
linelen -= leftmargin.length();
for (const char* s = line; s < ends; s++) {
const char* start = s;
while (s < ends && isspace((unsigned char) *s))
s++;
const char* word = s;
while (s < ends && !isspace((unsigned char) *s))
s++;
if (s - line > linelen && start > line) {
*this << leftmargin;
append(line, start - line);
*this << '\n';
line = word;
}
}
if (line < text.end()) {
*this << leftmargin;
append(line, text.end() - line);
*this << '\n';
}
}
} // namespace lcdf