egra: post rebuild event on minimisation (because minimised windows won't do it)
[iv.d.git] / dynstring.d
blob0660ca10fb6e4c21e876fab2faebab5f7ba744e6
1 /*
2 * simple malloced refcounted dynamic strings
4 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
5 * Understanding is not required. Only obedience.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 module iv.dynstring;
21 private import iv.atomic;
22 private import iv.strex;
24 //version = dynstring_debug;
27 // ////////////////////////////////////////////////////////////////////////// //
28 alias dynstr = dynstring;
30 public struct dynstring {
31 private:
32 static struct Data {
33 usize udata = void;
34 uint rc = void;
35 uint used = void;
36 uint alloted = void;
38 inout(char)* ptr () inout pure nothrow @trusted @nogc { pragma(inline, true); return cast(char*)udata; }
39 void ptr (void *v) pure nothrow @trusted @nogc { pragma(inline, true); udata = cast(usize)v; }
42 private:
43 usize udata = 0;
44 uint ofs = 0, len = 0; // for slices
46 nothrow @trusted @nogc:
47 private:
48 inout(Data)* datap () inout pure { pragma(inline, true); return cast(Data*)udata; }
49 void datap (Data *v) pure { pragma(inline, true); udata = cast(usize)v; }
51 void outofmem () const {
52 import core.exception : onOutOfMemoryErrorNoGC;
53 version(dynstring_test) assert(0, "out of memory");
54 onOutOfMemoryErrorNoGC();
57 void freeData () {
58 if (udata) {
59 version(dynstring_debug) {
60 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "freeData: %08x; used=%u; alloted=%u\n", cast(uint)udata, (cast(Data*)udata).used, (cast(Data*)udata).alloted);
62 import core.stdc.stdlib : free;
63 import core.stdc.string : memset;
64 assert((cast(Data*)udata).used <= (cast(Data*)udata).alloted);
65 if ((cast(Data*)udata).udata) free((cast(Data*)udata).ptr);
66 memset((cast(Data*)udata), 0, Data.sizeof);
67 //udata = 0;
68 //ofs = len = 0;
72 void incRef () const {
73 pragma(inline, true);
74 if (udata) {
75 atomicFetchAdd((cast(Data*)udata).rc, 1);
76 version(dynstring_debug) { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "incRef: %08x; rc=%u; used=%u; alloted=%u; len=%u; ofs=%u\n", cast(uint)udata, datap.rc, datap.used, datap.alloted, len, ofs); }
80 void decRef () {
81 pragma(inline, true);
82 if (udata) {
83 version(dynstring_debug) { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "decRef: %08x; rc=%u; used=%u; alloted=%u; len=%u; ofs=%u\n", cast(uint)udata, datap.rc, datap.used, datap.alloted, len, ofs); }
84 if (atomicFetchSub((cast(Data*)udata).rc, 1) == 1) freeData();
85 udata = 0;
87 ofs = len = 0;
90 private:
91 bool isMyData (const(void)* ptr) const pure {
92 pragma(inline, true);
93 if (!ptr || !udata || !datap.udata) return false;
94 immutable usize cc = cast(usize)ptr;
95 return (cc >= datap.udata && cc < datap.udata+datap.alloted);
98 static uint alignSize(bool overalloc) (uint sz) pure {
99 pragma(inline, true);
100 static if (overalloc) {
101 if (sz <= 256) return (sz|0x1fU)+1U;
102 else if (sz <= 1024) return (sz|0x3fU)+1U;
103 else if (sz <= 8192) return (sz|0x1ffU)+1U;
104 else if (sz <= 16384) return (sz|0x3ffU)+1U;
105 else if (sz <= 32768) return (sz|0xfffU)+1U;
106 else return (sz|0x1fffU)+1U;
107 } else {
108 return (sz|0x1fU)+1U;
112 // make sure that we have a room for at least `sz` chars
113 void ensureRoomFor(bool overalloc) (uint sz) {
114 if (sz == 0) return;
115 if (sz > 0x3fff_ffffU) outofmem();
116 Data* dp = void;
117 // if empty, allocate new storage and data
118 if (!udata) {
119 import core.stdc.stdlib : calloc, malloc, free;
120 assert(len == 0);
121 dp = cast(Data*)calloc(1, Data.sizeof);
122 if (dp is null) outofmem();
123 dp.rc = 1U;
124 sz = alignSize!overalloc(sz);
125 void *np = malloc(sz);
126 if (np is null) outofmem();
127 dp.ptr = np;
128 dp.alloted = sz;
129 udata = cast(usize)dp;
130 ofs = len = 0; // just in case
131 version(dynstring_debug) {
132 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "NEWSTR000: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; sz=%u\n",
133 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0), sz);
135 return;
137 // if shared, allocate completely new data and storage
138 dp = datap;
139 if (atomicLoad(dp.rc) != 1) {
140 version(dynstring_debug) {
141 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "NEWSTR100: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; sz=%u\n",
142 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0), sz);
144 import core.stdc.stdlib : calloc, malloc, free;
145 if (len > 0x3fff_ffffU || uint.max-len <= sz || len+sz > 0x3fff_ffffU) outofmem();
146 sz = alignSize!overalloc(len+sz);
147 assert(sz > len);
148 Data* dpnew = cast(Data*)calloc(1, Data.sizeof);
149 if (dpnew is null) outofmem();
150 dpnew.rc = 1U;
151 void *np = malloc(sz);
152 if (np is null) outofmem();
153 dpnew.ptr = np;
154 dpnew.alloted = sz;
155 dpnew.used = len;
156 if (len) dpnew.ptr[0..len] = dp.ptr[ofs..ofs+len];
157 decRef();
158 udata = cast(usize)dpnew;
159 ofs = 0;
160 len = dpnew.used;
161 version(dynstring_debug) {
162 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "NEWSTR101: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; sz=%u\n",
163 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0), sz);
165 return;
167 // if unshared slice, normalise storage
168 if (ofs) {
169 version(dynstring_debug) { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "strOFS: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u\n", cast(uint)dp, dp.rc, ofs, len, dp.used, dp.alloted); }
170 import core.stdc.string : memmove;
171 if (len) memmove(dp.ptr, dp.ptr+ofs, len);
172 ofs = 0;
174 if (len != dp.used) {
175 version(dynstring_debug) { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "strLEN: udata=0x%08x (0x%08x); rc=%u; ofs=%u; len=%u; used=%u; alloted=%u\n", cast(uint)dp, udata, dp.rc, ofs, len, dp.used, dp.alloted); }
176 assert(len <= dp.used);
177 dp.used = len;
179 // not shared, not slice, expand storage
180 assert(ofs == 0);
181 assert(len == dp.used);
182 if (len > 0x3fff_ffffU || uint.max-len <= sz || len+sz > 0x3fff_ffffU) outofmem();
183 sz = alignSize!overalloc(len+sz);
184 assert(sz > len);
185 if (sz > dp.alloted) {
186 import core.stdc.stdlib : realloc;
187 void *np = realloc(dp.ptr, sz);
188 if (np is null) outofmem();
189 dp.ptr = np;
190 dp.alloted = sz;
191 version(dynstring_debug) {
192 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "NEWSTR200: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; sz=%u\n",
193 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0), sz);
198 enum MixinEnsureUniqueBuf(string bname) = `
199 dynstring tmpstr;
200 if (isMyData(`~bname~`.ptr)) {
201 version(dynstring_debug) { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "MYDATA!: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; ptr=0x%08x\n",
202 cast(uint)udata, datap.rc, ofs, len, datap.used, datap.alloted, cast(uint)`~bname~`.ptr); }
203 tmpstr.set(`~bname~`);
204 `~bname~` = cast(typeof(`~bname~`))tmpstr.getData();
205 assert(!isMyData(`~bname~`.ptr));
209 public:
210 alias getData this;
212 public:
213 this() (in auto ref dynstring s) { pragma(inline, true); if (s.len) { udata = s.udata; ofs = s.ofs; len = s.len; incRef(); } }
215 this (const(char)[] s) {
216 if (s.length) {
217 ensureRoomFor!false(s.length);
218 datap.ptr[0..s.length] = s[];
219 len = datap.used = cast(uint)s.length;
220 ofs = 0;
224 this (in char ch) { ensureRoomFor!false(1); datap.ptr[0] = ch; len = datap.used = 1; ofs = 0; }
226 this (this) { pragma(inline, true); if (udata) incRef(); }
227 ~this () { pragma(inline, true); if (udata) decRef(); }
229 void clear () { pragma(inline, true); decRef(); }
231 void makeUnique(bool compress=false) () {
232 if (!udata) { ofs = len = 0; return; }
233 if (!len) { decRef(); return; }
234 Data* dp = datap;
235 // non-shared?
236 if (atomicLoad(dp.rc) == 1) {
237 // normalise storage
238 if (ofs) {
239 import core.stdc.string : memmove;
240 if (len) memmove(dp.ptr, dp.ptr+ofs, len);
241 ofs = 0;
243 if (len != dp.used) { assert(len <= dp.used); dp.used = len; }
244 static if (compress) {
245 if ((dp.used|0x1fU)+1U < dp.alloted) {
246 // realloc
247 import core.stdc.stdlib : realloc;
248 immutable uint sz = (dp.used|0x1fU)+1U;
249 void *np = realloc(dp.ptr, sz);
250 if (np !is null) {
251 dp.ptr = np;
252 dp.alloted = sz;
253 version(dynstring_debug) {
254 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "UNIQUERE: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u\n",
255 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0));
260 return;
262 // allocate new storage
263 dynstring tmpstr;
264 tmpstr.set(getData());
265 decRef();
266 udata = tmpstr.udata;
267 ofs = tmpstr.ofs;
268 len = tmpstr.len;
269 tmpstr.udata = 0;
270 tmpstr.ofs = tmpstr.len = 0;
273 //WARNING! MAKE SURE THAT YOU KNOW WHAT YOU'RE DOING!
274 char *makeUniquePointer () { pragma(inline, true); makeUnique(); return (len ? datap.ptr : null); }
276 bool isSlice () const pure { pragma(inline, true); return (!!ofs); }
277 bool isShared () const pure { pragma(inline, true); return (udata && atomicLoad(datap.rc) != 1); }
278 uint capacity () const pure { pragma(inline, true); return (udata ? datap.alloted : 0); }
280 void reserve(bool overalloc=false) (uint newsz) {
281 pragma(inline, true);
282 if (newsz && newsz < 0x3fff_ffffU && newsz > len) ensureRoomFor!overalloc(newsz-len);
285 void append(bool overalloc=true) (const(void)[] buf) {
286 if (buf.length == 0) return;
287 if (buf.length > cast(usize)0x3fff_ffff) outofmem();
288 //if (udata && cast(usize)0x3000_0000-datap.used < buf.length) outofmem();
289 version(dynstring_debug) {
290 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "APPEND000: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; ptr=0x%08x; buflen=%u\n",
291 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0), cast(uint)buf.ptr, cast(uint)buf.length);
293 mixin(MixinEnsureUniqueBuf!"buf");
294 version(dynstring_debug) {
295 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "APPEND001: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; ptr=0x%08x; buflen=%u\n",
296 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0), cast(uint)buf.ptr, cast(uint)buf.length);
298 ensureRoomFor!overalloc(buf.length);
299 version(dynstring_debug) {
300 import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "APPEND002: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u; ptr=0x%08x; buflen=%u\n",
301 cast(uint)udata, (udata ? datap.rc : 0), ofs, len, (udata ? datap.used : 0), (udata ? datap.alloted : 0), cast(uint)buf.ptr, cast(uint)buf.length);
303 assert(ofs == 0);
304 Data* data = datap;
305 assert(data !is null && data.used+buf.length <= data.alloted);
306 assert(len == datap.used);
307 data.ptr[len..len+buf.length] = cast(const(char)[])buf;
308 data.used += cast(uint)buf.length;
309 len += cast(uint)buf.length;
310 assert(len <= data.used);
312 void append(bool overalloc=true) (in char ch) { pragma(inline, true); append!overalloc((&ch)[0..1]); }
314 void set (const(void)[] buf) {
315 if (buf.length > cast(usize)0x3fff_ffff) outofmem();
316 if (buf.length == 0) { decRef(); return; }
317 if (udata) {
318 if (isMyData(buf.ptr)) {
319 // do not copy data if it is not necessary
320 if (ofs == 0 && buf.ptr == datap.ptr && buf.length <= datap.used) {
321 if (len != cast(uint)buf.length) {
322 len = cast(uint)buf.length;
323 if (atomicLoad(datap.rc) == 1) datap.used = len;
325 return;
327 dynstring tmpstr;
328 tmpstr.set(buf);
329 decRef();
330 udata = tmpstr.udata;
331 ofs = tmpstr.ofs;
332 len = tmpstr.len;
333 tmpstr.udata = 0;
334 tmpstr.ofs = tmpstr.len = 0;
335 return;
337 decRef();
339 assert(udata == 0 && ofs == 0 && len == 0);
340 ensureRoomFor!false(buf.length);
341 datap.ptr[0..buf.length] = cast(const(char)[])buf;
342 len = datap.used = cast(uint)buf.length;
344 void set (in char ch) { pragma(inline, true); set((&ch)[0..1]); }
346 uint length () const pure { pragma(inline, true); return len; }
348 void length (in uint newlen) {
349 pragma(inline, true);
350 if (newlen < len) {
351 len = newlen;
352 } else if (newlen != len) {
353 ensureRoomFor!false(newlen-len);
354 assert(len < newlen);
355 assert(ofs == 0);
356 assert(newlen <= datap.alloted);
357 datap.ptr[len..newlen] = 0;
358 len = datap.used = newlen;
362 const(char)[] getData () const pure { pragma(inline, true); return (len ? datap.ptr[ofs..ofs+len] : null); }
363 const(ubyte)[] getBytes () const pure { pragma(inline, true); return cast(const(ubyte)[])(len ? datap.ptr[ofs..ofs+len] : null); }
365 void opAssign() (const(char)[] s) { pragma(inline, true); set(s); }
366 void opAssign() (in char ch) { pragma(inline, true); set(ch); }
368 void opAssign() (in auto ref dynstring s) {
369 pragma(inline, true);
370 if (s.udata != udata) {
371 if (s.len) {
372 s.incRef();
373 decRef();
374 udata = s.udata;
375 ofs = s.ofs;
376 len = s.len;
377 } else {
378 decRef();
380 } else {
381 // same data, so RC is already valid
382 ofs = s.ofs;
383 len = s.len;
387 void opOpAssign(string op:"~") (const(char)[] s) { pragma(inline, true); if (s.length) append(s); }
388 void opOpAssign(string op:"~") (in char ch) { pragma(inline, true); append(ch); }
389 void opOpAssign(string op:"~") (in auto ref dynstring s) { pragma(inline, true); if (s.len) append(s.getData()); }
391 const(char)[] opCast(T=const(char)[]) () const pure { pragma(inline, true); return getData(); }
392 const(ubyte)[] opCast(T=const(ubyte)[]) () const pure { pragma(inline, true); return getBytes(); }
394 bool opCast(T=bool) () const pure { pragma(inline, true); return (len != 0); }
396 bool opEqual (const(char)[] other) const pure { pragma(inline, true); return (other.length == len && other[] == getBytes()[]); }
397 bool opEqual() (in auto ref dynstring other) const pure { pragma(inline, true); return (other.len == len && other.getBytes()[] == getBytes()[]); }
399 int opCmp (const(char)[] other) const pure {
400 if (len == 0) return (other.length ? -1 : 0);
401 if (other.length == 0) return 1;
402 if (len == other.length && datap.ptr+ofs == other.ptr) return 0;
403 import core.stdc.string : memcmp;
404 immutable int cres = memcmp(datap.ptr+ofs, other.ptr, (len <= other.length ? len : other.length));
405 if (cres != 0) return (cres < 0 ? -1 : +1);
406 return (len < other.length ? -1 : len > other.length ? +1 : 0);
409 int opCmp() (in auto ref dynstring other) const { pragma(inline, true); return opCmp(other.getData); }
411 dynstring opBinary(string op:"~") (in auto ref dynstring other) const {
412 pragma(inline, true);
413 dynstring res;
414 if (!len) {
415 res.set(other.getData);
416 } else if (!other.len) {
417 res.set(getData);
418 } else {
419 if (uint.max-len <= other.len) outofmem();
420 res.ensureRoomFor!false(len+other.len);
421 res.datap.ptr[0..len] = getData();
422 res.datap.ptr[len..len+other.len] = other.getData();
423 assert(res.ofs == 0);
424 res.datap.used = res.len = len+other.len;
426 return res;
429 dynstring opBinary(string op:"~") (const(char)[] other) const {
430 pragma(inline, true);
431 dynstring res;
432 if (!len) {
433 res.set(other);
434 } else if (!other.length) {
435 res.set(getData);
436 } else {
437 if (uint.max-len <= other.length) outofmem();
438 res.ensureRoomFor!false(len+cast(uint)other.length);
439 res.datap.ptr[0..len] = getData();
440 res.datap.ptr[len..len+other.length] = other.getData();
441 assert(res.ofs == 0);
442 res.datap.used = res.len = len+cast(uint)other.length;
444 return res;
447 char opIndex (in uint pos) const pure { pragma(inline, true); if (pos >= len) assert(0, "dynstring index out of bounds"); return datap.ptr[ofs+pos]; }
448 void opIndexAssign (in char ch, in uint pos) {
449 pragma(inline, true);
450 if (pos >= len) assert(0, "dynstring index out of bounds");
451 makeUnique();
452 assert(pos < len);
453 datap.ptr[ofs+pos] = ch;
456 dynstring opSlice (in uint lo, in uint hi) const {
457 if (lo > hi) assert(0, "dynstring slice index out of bounds (0)");
458 if (lo > len || hi > len) assert(0, "dynstring slice index out of bounds (1)");
459 dynstring res;
460 if (lo < hi) {
461 incRef();
462 res.udata = udata;
463 res.ofs = ofs+lo;
464 res.len = hi-lo;
466 return res;
469 uint opDollar () const pure { pragma(inline, true); return len; }
471 void appendQEncoded (const(void)[] ss) {
472 static bool isSpecial (immutable char ch) pure nothrow @safe @nogc {
473 return
474 ch < ' ' ||
475 ch >= 127 ||
476 ch == '\'' ||
477 ch == '`' ||
478 ch == '"' ||
479 ch == '\\' ||
480 ch == '@';
483 if (ss.length == 0) return;
484 const(char)[] s = cast(const(char)[])ss;
486 static immutable string hexd = "0123456789abcdef";
488 bool needWork = (s[0] == '=' || s[0] == '?');
489 if (!needWork) foreach (char ch; s) if (isSpecial(ch)) { needWork = true; break; }
491 if (!needWork) {
492 append(s);
493 } else {
494 mixin(MixinEnsureUniqueBuf!"ss");
495 append("=?UTF-8?Q?"); // quoted printable
496 foreach (char ch; s) {
497 if (ch <= ' ') ch = '_';
498 if (!isSpecial(ch) && ch != '=' && ch != '?') {
499 append(ch);
500 } else {
501 append("=");
502 append(hexd[(cast(ubyte)ch)>>4]);
503 append(hexd[(cast(ubyte)ch)&0x0f]);
506 append("?=");
510 void appendB64Encoded (const(void)[] buf, uint maxlinelen=76) {
511 static immutable string b64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
512 ubyte[3] bts = void;
513 uint btspos = 0;
514 uint linelen = 0;
516 void putB64Char (immutable char ch) nothrow @trusted @nogc {
517 if (maxlinelen && linelen >= maxlinelen) { append("\r\n"); linelen = 0; }
518 append(ch);
519 ++linelen;
522 void encodeChunk () nothrow @trusted @nogc {
523 if (btspos == 0) return;
524 putB64Char(b64alphabet.ptr[(bts.ptr[0]&0xfc)>>2]);
525 if (btspos == 1) {
526 putB64Char(b64alphabet.ptr[(bts.ptr[0]&0x03)<<4]);
527 /*static if (padding)*/ { putB64Char('='); putB64Char('='); }
528 } else {
529 // 2 or more
530 putB64Char(b64alphabet.ptr[((bts.ptr[0]&0x03)<<4)|((bts.ptr[1]&0xf0)>>4)]);
531 if (btspos == 2) {
532 putB64Char(b64alphabet.ptr[(bts.ptr[1]&0x0f)<<2]);
533 /*static if (padding)*/ putB64Char('=');
534 } else {
535 // 3 bytes
536 putB64Char(b64alphabet.ptr[((bts.ptr[1]&0x0f)<<2)|((bts.ptr[2]&0xc0)>>6)]);
537 putB64Char(b64alphabet.ptr[bts.ptr[2]&0x3f]);
540 btspos = 0;
543 mixin(MixinEnsureUniqueBuf!"buf");
545 foreach (immutable ubyte ib; (cast(const(ubyte)[])buf)[]) {
546 bts.ptr[btspos++] = ib;
547 if (btspos == 3) encodeChunk();
549 if (btspos != 0) encodeChunk();
551 if (maxlinelen) append("\r\n");
554 void appendNum(T) (in T v) if (__traits(isIntegral, T)) {
555 import core.stdc.stdio : snprintf;
556 static if (T.sizeof <= 4) {
557 char[32] buf = void;
558 static if (__traits(isUnsigned, T)) {
559 auto len = snprintf(buf.ptr, buf.length, "%u", cast(uint)v);
560 } else {
561 auto len = snprintf(buf.ptr, buf.length, "%d", cast(int)v);
563 append(buf[0..len]);
564 } else {
565 char[128] buf = void;
566 static if (__traits(isUnsigned, T)) {
567 auto len = snprintf(buf.ptr, buf.length, "%llu", cast(ulong)v);
568 } else {
569 auto len = snprintf(buf.ptr, buf.length, "%lld", cast(long)v);
571 append(buf[0..len]);
575 void removeASCIICtrls (in char repch=' ') {
576 static bool isCtrl (in char ch) pure nothrow @safe @nogc {
577 pragma(inline, true);
578 return (ch < 32 && ch != '\t' && ch != '\n');
581 bool needWork = false;
582 foreach (immutable char ch; getData) if (isCtrl(ch)) { needWork = true; break; }
583 if (!needWork) return;
585 makeUnique();
586 char[] buf = cast(char[])datap.ptr[0..datap.used];
587 foreach (ref char ch; buf) if (isCtrl(ch)) ch = repch;
590 // only for ASCII
591 void lowerInPlace () {
592 bool needWork = false;
593 foreach (immutable char ch; getData) if (ch >= 'A' && ch <= 'Z') { needWork = true; break; }
594 if (!needWork) return;
596 makeUnique();
597 char[] buf = cast(char[])datap.ptr[0..datap.used];
598 foreach (ref char ch; buf) if (ch >= 'A' && ch <= 'Z') ch += 32;
601 // only for ASCII
602 void upperInPlace () {
603 bool needWork = false;
604 foreach (immutable char ch; getData) if (ch >= 'a' && ch <= 'z') { needWork = true; break; }
605 if (!needWork) return;
607 makeUnique();
608 char[] buf = cast(char[])datap.ptr[0..datap.used];
609 foreach (ref char ch; buf) if (ch >= 'a' && ch <= 'z') ch -= 32;
612 dynstring xstripright () const {
613 dynstring res;
614 if (len == 0) return res;
615 if (opIndex(len-1) > ' ') { res = this; return res; }
616 const(char)[] dt = getData().xstripright;
617 res.set(dt);
618 return res;
621 dynstring xstripleft () const {
622 dynstring res;
623 if (len == 0) return res;
624 if (opIndex(0) > ' ') { res = this; return res; }
625 const(char)[] dt = getData().xstripleft;
626 res.set(dt);
627 return res;
630 dynstring xstrip () const {
631 dynstring res;
632 if (len == 0) return res;
633 if (opIndex(0) > ' ' && opIndex(len-1) > ' ') { res = this; return res; }
634 const(char)[] dt = getData().xstrip;
635 res.set(dt);
636 return res;
639 // this can never return `int.min` from conversion!
640 int toInt (int defval=int.min) const pure {
641 const(char)[] dt = getData().xstrip;
642 if (dt.length == 0) return defval;
643 bool neg = false;
644 if (dt[0] == '+') dt = dt[1..$];
645 else if (dt[0] == '-') { neg = true; dt = dt[1..$]; }
646 bool wasDigit = false;
647 int n = 0;
648 foreach (immutable char ch; dt) {
649 if (ch == '_' || ch == ' ' || ch == ',') continue;
650 immutable int dg = digitInBase(ch, 10);
651 if (dg < 0) return defval;
652 wasDigit = true;
653 n *= 10;
654 if (n < 0) return defval;
655 n += dg;
656 if (n < 0) return defval;
658 if (!wasDigit) return defval;
659 if (neg) n = -n;
660 return n;
665 version(dynstring_test) {
666 import iv.dynstring;
667 import iv.vfs.io;
669 void main () {
670 dynstring tmp;
671 tmp = "abc";
672 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
673 tmp ~= "d";
674 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
675 tmp = tmp[1..3];
676 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
677 tmp[1] = '!';
678 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
679 tmp.makeUnique();
680 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
682 tmp = "abcdefgh";
683 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
684 tmp = tmp[1..$-1];
685 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
686 tmp = tmp[1..$-1];
687 writefln("tmp: udata=0x%08x; (%u) ofs=%u; len=%u; <%s>", tmp.udata, (tmp.udata ? tmp.datap.rc : 0), tmp.ofs, tmp.len, tmp.getData);
688 writeln("tmp[2]='", tmp[2], "'");
690 tmp = " goo ";
691 tmp = tmp.xstripright;
692 writeln("tmp: <", tmp.getData, ">");
694 tmp = " goo ";
695 tmp = tmp.xstripleft;
696 writeln("tmp: <", tmp.getData, ">");
698 tmp = " goo ";
699 tmp = tmp.xstrip;
700 writeln("tmp: <", tmp.getData, ">");