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/>.
21 private import iv
.atomic
;
22 private import iv
.strex
;
24 //version = dynstring_debug;
25 //version = dynstring_debug_clear;
26 //version = dynstring_more_asserts;
29 // ////////////////////////////////////////////////////////////////////////// //
30 alias dynstr
= dynstring
;
32 public struct dynstring
{
39 // string data follows
41 inout(char)* ptr () inout pure nothrow @trusted @nogc { pragma(inline
, true); return (cast(inout(char)*)&this)+Data
.sizeof
; }
43 static assert(Data
.sizeof
== 16, "invalid `dynstring.Data` size");
47 uint ofs
= 0, len
= 0; // for slices
49 nothrow @trusted @nogc:
51 inout(Data
)* datap () inout pure { pragma(inline
, true); return cast(Data
*)udata
; }
53 void outofmem () const {
54 import core
.exception
: onOutOfMemoryErrorNoGC
;
55 version(dynstring_test
) assert(0, "out of memory");
56 onOutOfMemoryErrorNoGC();
59 void incRef () const {
62 atomicFetchAdd((cast(Data
*)udata
).rc
, 1);
63 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
); }
70 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
); }
71 if (atomicFetchSub((cast(Data
*)udata
).rc
, 1) == 1) {
72 version(dynstring_debug
) {
73 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
);
75 import core
.stdc
.stdlib
: free
;
76 version(dynstring_debug_clear
) import core
.stdc
.string
: memset
;
77 version(dynstring_debug_clear
) assert((cast(Data
*)udata
).used
<= (cast(Data
*)udata
).alloted
);
78 version(dynstring_debug_clear
) memset((cast(Data
*)udata
), 0, Data
.sizeof
);
79 free((cast(Data
*)udata
));
87 bool isMyData (const(void)* ptr
) const pure {
89 if (ptr
is null ||
!udata
) return false;
90 immutable usize cc
= cast(usize
)ptr
;
91 return (cc
>= udata
+Data
.sizeof
&& cc
< udata
+Data
.sizeof
+datap
.alloted
);
94 static uint alignSize(bool overalloc
) (uint sz
) pure {
96 static if (overalloc
) {
97 if (sz
<= 256) return (sz|
0x1fU
)+1U;
98 else if (sz
<= 1024) return (sz|
0x3fU
)+1U;
99 else if (sz
<= 8192) return (sz|
0x1ffU
)+1U;
100 else if (sz
<= 16384) return (sz|
0x3ffU
)+1U;
101 else if (sz
<= 32768) return (sz|
0xfffU
)+1U;
102 else return (sz|
0x1fffU
)+1U;
104 return (sz|
0x1fU
)+1U;
108 // make sure that we have a room for at least `sz` chars
109 void ensureRoomFor(bool overalloc
) (uint sz
) {
111 if (sz
> 0x3fff_ffffU
) outofmem();
112 // if empty, allocate new storage and data
114 import core
.stdc
.stdlib
: malloc
;
115 version(dynstring_more_asserts
) assert(len
== 0);
116 sz
= alignSize
!overalloc(sz
);
117 Data
* xdp
= cast(Data
*)malloc(Data
.sizeof
+sz
);
118 if (xdp
is null) outofmem();
123 udata
= cast(usize
)xdp
;
124 ofs
= len
= 0; // just in case
125 version(dynstring_debug
) {
126 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",
127 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
131 // if shared, allocate completely new data and storage
133 if (atomicLoad(dp
.rc
) != 1) {
134 version(dynstring_debug
) {
135 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",
136 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
138 import core
.stdc
.stdlib
: malloc
;
139 if (len
> 0x3fff_ffffU ||
uint.max
-len
<= sz || len
+sz
> 0x3fff_ffffU
) outofmem();
140 sz
= alignSize
!overalloc(len
+sz
);
141 version(dynstring_more_asserts
) assert(sz
> len
);
142 Data
* dpnew
= cast(Data
*)malloc(Data
.sizeof
+sz
);
143 if (dpnew
is null) outofmem();
148 if (len
) dpnew
.ptr
[0..len
] = dp
.ptr
[ofs
..ofs
+len
];
150 udata
= cast(usize
)dpnew
;
151 ofs
= 0; // always reset by `decRef()`, but meh...
152 len
= dpnew
.used
; // it was reset by `decRef()`, so restore it
153 version(dynstring_debug
) {
154 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",
155 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
159 // if unshared slice, normalise storage
161 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
); }
162 import core
.stdc
.string
: memmove
;
163 if (len
) memmove(dp
.ptr
, dp
.ptr
+ofs
, len
);
166 if (len
!= dp
.used
) {
167 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
); }
168 version(dynstring_more_asserts
) assert(len
<= dp
.used
);
171 // not shared, not slice, expand storage
172 version(dynstring_more_asserts
) assert(ofs
== 0);
173 version(dynstring_more_asserts
) assert(len
== dp
.used
);
174 if (len
> 0x3fff_ffffU ||
uint.max
-len
<= sz || len
+sz
> 0x3fff_ffffU
) outofmem();
175 sz
= alignSize
!overalloc(len
+sz
);
176 version(dynstring_more_asserts
) assert(sz
> len
);
177 if (sz
> dp
.alloted
) {
178 import core
.stdc
.stdlib
: realloc
;
179 Data
* np
= cast(Data
*)realloc(dp
, Data
.sizeof
+sz
);
180 if (np
is null) outofmem();
182 udata
= cast(usize
)np
;
183 version(dynstring_debug
) {
184 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",
185 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
190 enum MixinEnsureUniqueBuf(string bname
) = `
192 if (isMyData(`~bname
~`.ptr)) {
193 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",
194 cast(uint)udata, datap.rc, ofs, len, datap.used, datap.alloted, cast(uint)`~bname
~`.ptr); }
195 tmpstr.set(`~bname
~`);
196 `~bname
~` = cast(typeof(`~bname
~`))tmpstr.getData();
197 version(dynstring_more_asserts) assert(!isMyData(`~bname
~`.ptr));
205 this() (in auto ref dynstring s
) { pragma(inline
, true); if (s
.len
) { udata
= s
.udata
; ofs
= s
.ofs
; len
= s
.len
; incRef(); } }
207 this (const(char)[] s
) {
209 ensureRoomFor
!false(s
.length
);
210 datap
.ptr
[0..s
.length
] = s
[];
211 len
= datap
.used
= cast(uint)s
.length
;
216 this (in char ch
) { ensureRoomFor
!false(1); datap
.ptr
[0] = ch
; len
= datap
.used
= 1; ofs
= 0; }
218 this (this) { pragma(inline
, true); if (udata
) incRef(); }
219 ~this () { pragma(inline
, true); if (udata
) decRef(); }
221 void clear () { pragma(inline
, true); decRef(); }
223 void makeUnique(bool compress
=false) () {
224 if (!udata
) { ofs
= len
= 0; return; }
225 if (!len
) { decRef(); return; }
228 if (atomicLoad(dp
.rc
) == 1) {
231 import core
.stdc
.string
: memmove
;
232 if (len
) memmove(dp
.ptr
, dp
.ptr
+ofs
, len
);
235 if (len
!= dp
.used
) { version(dynstring_more_asserts
) assert(len
<= dp
.used
); dp
.used
= len
; }
236 static if (compress
) {
237 if ((dp
.used|
0x1fU
)+1U < dp
.alloted
) {
239 import core
.stdc
.stdlib
: realloc
;
240 immutable uint sz
= (dp
.used|
0x1fU
)+1U;
241 Data
* np
= cast(Data
*)realloc(dp
, Data
.sizeof
+sz
);
244 udata
= cast(usize
)np
;
245 version(dynstring_debug
) {
246 import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "UNIQUERE: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u\n",
247 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0));
254 // allocate new storage
256 tmpstr
.set(getData());
258 udata
= tmpstr
.udata
;
262 tmpstr
.ofs
= tmpstr
.len
= 0;
265 //WARNING! MAKE SURE THAT YOU KNOW WHAT YOU'RE DOING!
266 char *makeUniquePointer () { pragma(inline
, true); makeUnique(); return (len ? datap
.ptr
: null); }
268 bool isSlice () const pure { pragma(inline
, true); return !!ofs
; }
269 bool isShared () const pure { pragma(inline
, true); return (udata
&& atomicLoad(datap
.rc
) != 1); }
270 uint capacity () const pure { pragma(inline
, true); return (udata ? datap
.alloted
: 0); }
272 void reserve(bool overalloc
=false) (uint newsz
) {
273 pragma(inline
, true);
274 if (newsz
&& newsz
< 0x3fff_ffffU
&& newsz
> len
) ensureRoomFor
!overalloc(newsz
-len
);
277 void append(bool overalloc
=true) (const(void)[] buf
) {
278 if (buf
.length
== 0) return;
279 if (buf
.length
> cast(usize
)0x3fff_ffff
) outofmem();
280 //if (udata && cast(usize)0x3000_0000-datap.used < buf.length) outofmem();
281 version(dynstring_debug
) {
282 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",
283 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
);
285 mixin(MixinEnsureUniqueBuf
!"buf");
286 version(dynstring_debug
) {
287 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",
288 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
);
290 ensureRoomFor
!overalloc(buf
.length
);
291 version(dynstring_debug
) {
292 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",
293 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
);
295 version(dynstring_more_asserts
) assert(ofs
== 0);
297 version(dynstring_more_asserts
) assert(data
!is null && data
.used
+buf
.length
<= data
.alloted
);
298 version(dynstring_more_asserts
) assert(len
== datap
.used
);
299 data
.ptr
[len
..len
+buf
.length
] = cast(const(char)[])buf
;
300 data
.used
+= cast(uint)buf
.length
;
301 len
+= cast(uint)buf
.length
;
302 version(dynstring_more_asserts
) assert(len
<= data
.used
);
304 void append(bool overalloc
=true) (in char ch
) { pragma(inline
, true); append
!overalloc((&ch
)[0..1]); }
306 void set (const(void)[] buf
) {
307 if (buf
.length
> cast(usize
)0x3fff_ffff
) outofmem();
308 if (buf
.length
== 0) { decRef(); return; }
310 if (isMyData(buf
.ptr
)) {
311 // do not copy data if it is not necessary
312 if (ofs
== 0 && buf
.ptr
== datap
.ptr
&& buf
.length
<= datap
.used
) {
313 if (len
!= cast(uint)buf
.length
) {
314 len
= cast(uint)buf
.length
;
315 if (atomicLoad(datap
.rc
) == 1) datap
.used
= len
;
322 udata
= tmpstr
.udata
;
326 tmpstr
.ofs
= tmpstr
.len
= 0;
331 version(dynstring_more_asserts
) assert(udata
== 0 && ofs
== 0 && len
== 0);
332 ensureRoomFor
!false(buf
.length
);
333 datap
.ptr
[0..buf
.length
] = cast(const(char)[])buf
;
334 len
= datap
.used
= cast(uint)buf
.length
;
336 void set (in char ch
) { pragma(inline
, true); set((&ch
)[0..1]); }
338 uint length () const pure { pragma(inline
, true); return len
; }
340 void length (in uint newlen
) {
341 pragma(inline
, true);
344 } else if (newlen
!= len
) {
345 ensureRoomFor
!false(newlen
-len
);
346 version(dynstring_more_asserts
) assert(len
< newlen
);
347 version(dynstring_more_asserts
) assert(ofs
== 0);
348 version(dynstring_more_asserts
) assert(newlen
<= datap
.alloted
);
349 datap
.ptr
[len
..newlen
] = 0;
350 len
= datap
.used
= newlen
;
354 const(char)[] getData () const pure { pragma(inline
, true); return (len ? datap
.ptr
[ofs
..ofs
+len
] : null); }
355 const(ubyte)[] getBytes () const pure { pragma(inline
, true); return cast(const(ubyte)[])(len ? datap
.ptr
[ofs
..ofs
+len
] : null); }
357 void opAssign() (const(char)[] s
) { pragma(inline
, true); set(s
); }
358 void opAssign() (in char ch
) { pragma(inline
, true); set(ch
); }
360 void opAssign() (in auto ref dynstring s
) {
361 pragma(inline
, true);
362 if (s
.udata
!= udata
) {
373 // same data, so RC is already valid
379 void opOpAssign(string op
:"~") (const(char)[] s
) { pragma(inline
, true); if (s
.length
) append(s
); }
380 void opOpAssign(string op
:"~") (in char ch
) { pragma(inline
, true); append(ch
); }
381 void opOpAssign(string op
:"~") (in auto ref dynstring s
) { pragma(inline
, true); if (s
.len
) append(s
.getData()); }
383 const(char)[] opCast(T
=const(char)[]) () const pure { pragma(inline
, true); return getData(); }
384 const(ubyte)[] opCast(T
=const(ubyte)[]) () const pure { pragma(inline
, true); return getBytes(); }
386 bool opCast(T
=bool) () const pure { pragma(inline
, true); return (len
!= 0); }
388 bool opEqual (const(char)[] other
) const pure { pragma(inline
, true); return (other
.length
== len
&& other
[] == getBytes()[]); }
389 bool opEqual() (in auto ref dynstring other
) const pure { pragma(inline
, true); return (other
.len
== len
&& other
.getBytes()[] == getBytes()[]); }
391 int opCmp (const(char)[] other
) const pure {
392 if (len
== 0) return (other
.length ?
-1 : 0);
393 if (other
.length
== 0) return 1;
394 if (len
== other
.length
&& datap
.ptr
+ofs
== other
.ptr
) return 0;
395 import core
.stdc
.string
: memcmp
;
396 immutable int cres
= memcmp(datap
.ptr
+ofs
, other
.ptr
, (len
<= other
.length ? len
: other
.length
));
397 if (cres
!= 0) return (cres
< 0 ?
-1 : +1);
398 return (len
< other
.length ?
-1 : len
> other
.length ?
+1 : 0);
401 int opCmp() (in auto ref dynstring other
) const { pragma(inline
, true); return opCmp(other
.getData
); }
403 dynstring
opBinary(string op
:"~") (in auto ref dynstring other
) const {
404 pragma(inline
, true);
407 res
.set(other
.getData
);
408 } else if (!other
.len
) {
411 if (uint.max
-len
<= other
.len
) outofmem();
412 res
.ensureRoomFor
!false(len
+other
.len
);
413 res
.datap
.ptr
[0..len
] = getData();
414 res
.datap
.ptr
[len
..len
+other
.len
] = other
.getData();
415 version(dynstring_more_asserts
) assert(res
.ofs
== 0);
416 res
.datap
.used
= res
.len
= len
+other
.len
;
421 dynstring
opBinary(string op
:"~") (const(char)[] other
) const {
422 pragma(inline
, true);
426 } else if (!other
.length
) {
429 if (uint.max
-len
<= other
.length
) outofmem();
430 res
.ensureRoomFor
!false(len
+cast(uint)other
.length
);
431 res
.datap
.ptr
[0..len
] = getData();
432 res
.datap
.ptr
[len
..len
+other
.length
] = other
;
433 version(dynstring_more_asserts
) assert(res
.ofs
== 0);
434 res
.datap
.used
= res
.len
= len
+cast(uint)other
.length
;
439 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
]; }
440 void opIndexAssign (in char ch
, in uint pos
) {
441 pragma(inline
, true);
442 if (pos
>= len
) assert(0, "dynstring index out of bounds");
444 version(dynstring_more_asserts
) assert(pos
< len
);
445 datap
.ptr
[ofs
+pos
] = ch
;
448 dynstring
opSlice (in uint lo
, in uint hi
) const {
449 if (lo
> hi
) assert(0, "dynstring slice index out of bounds (0)");
450 if (lo
> len || hi
> len
) assert(0, "dynstring slice index out of bounds (1)");
461 uint opDollar () const pure { pragma(inline
, true); return len
; }
463 void appendQEncoded (const(void)[] ss
) {
464 static bool isSpecial (immutable char ch
) pure nothrow @safe @nogc {
475 if (ss
.length
== 0) return;
476 const(char)[] s
= cast(const(char)[])ss
;
478 static immutable string hexd
= "0123456789abcdef";
480 bool needWork
= (s
[0] == '=' || s
[0] == '?');
481 if (!needWork
) foreach (char ch
; s
) if (isSpecial(ch
)) { needWork
= true; break; }
486 mixin(MixinEnsureUniqueBuf
!"ss");
487 append("=?UTF-8?Q?"); // quoted printable
488 foreach (char ch
; s
) {
489 if (ch
<= ' ') ch
= '_';
490 if (!isSpecial(ch
) && ch
!= '=' && ch
!= '?') {
494 append(hexd
[(cast(ubyte)ch
)>>4]);
495 append(hexd
[(cast(ubyte)ch
)&0x0f]);
502 void appendB64Encoded (const(void)[] buf
, uint maxlinelen
=76) {
503 static immutable string b64alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
508 void putB64Char (immutable char ch
) nothrow @trusted @nogc {
509 if (maxlinelen
&& linelen
>= maxlinelen
) { append("\r\n"); linelen
= 0; }
514 void encodeChunk () nothrow @trusted @nogc {
515 if (btspos
== 0) return;
516 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0xfc)>>2]);
518 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0x03)<<4]);
519 /*static if (padding)*/ { putB64Char('='); putB64Char('='); }
522 putB64Char(b64alphabet
.ptr
[((bts.ptr
[0]&0x03)<<4)|
((bts.ptr
[1]&0xf0)>>4)]);
524 putB64Char(b64alphabet
.ptr
[(bts.ptr
[1]&0x0f)<<2]);
525 /*static if (padding)*/ putB64Char('=');
528 putB64Char(b64alphabet
.ptr
[((bts.ptr
[1]&0x0f)<<2)|
((bts.ptr
[2]&0xc0)>>6)]);
529 putB64Char(b64alphabet
.ptr
[bts.ptr
[2]&0x3f]);
535 mixin(MixinEnsureUniqueBuf
!"buf");
537 foreach (immutable ubyte ib
; (cast(const(ubyte)[])buf
)[]) {
538 bts.ptr
[btspos
++] = ib
;
539 if (btspos
== 3) encodeChunk();
541 if (btspos
!= 0) encodeChunk();
543 if (maxlinelen
) append("\r\n");
546 void appendNum(T
) (in T v
) if (__traits(isIntegral
, T
)) {
547 import core
.stdc
.stdio
: snprintf
;
548 static if (T
.sizeof
<= 4) {
550 static if (__traits(isUnsigned
, T
)) {
551 auto len
= snprintf(buf
.ptr
, buf
.length
, "%u", cast(uint)v
);
553 auto len
= snprintf(buf
.ptr
, buf
.length
, "%d", cast(int)v
);
557 char[128] buf
= void;
558 static if (__traits(isUnsigned
, T
)) {
559 auto len
= snprintf(buf
.ptr
, buf
.length
, "%llu", cast(ulong)v
);
561 auto len
= snprintf(buf
.ptr
, buf
.length
, "%lld", cast(long)v
);
567 void removeASCIICtrls (in char repch
=' ') {
568 static bool isCtrl (in char ch
) pure nothrow @safe @nogc {
569 pragma(inline
, true);
570 return (ch
< 32 && ch
!= '\t' && ch
!= '\n');
573 bool needWork
= false;
574 foreach (immutable char ch
; getData
) if (isCtrl(ch
)) { needWork
= true; break; }
575 if (!needWork
) return;
578 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
579 foreach (ref char ch
; buf
) if (isCtrl(ch
)) ch
= repch
;
583 void lowerInPlace () {
584 bool needWork
= false;
585 foreach (immutable char ch
; getData
) if (ch
>= 'A' && ch
<= 'Z') { needWork
= true; break; }
586 if (!needWork
) return;
589 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
590 foreach (ref char ch
; buf
) if (ch
>= 'A' && ch
<= 'Z') ch
+= 32;
594 void upperInPlace () {
595 bool needWork
= false;
596 foreach (immutable char ch
; getData
) if (ch
>= 'a' && ch
<= 'z') { needWork
= true; break; }
597 if (!needWork
) return;
600 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
601 foreach (ref char ch
; buf
) if (ch
>= 'a' && ch
<= 'z') ch
-= 32;
604 dynstring
xstripright () const {
606 if (len
== 0) return res
;
607 if (opIndex(len
-1) > ' ') { res
= this; return res
; }
608 const(char)[] dt = getData().xstripright
;
613 dynstring
xstripleft () const {
615 if (len
== 0) return res
;
616 if (opIndex(0) > ' ') { res
= this; return res
; }
617 const(char)[] dt = getData().xstripleft
;
622 dynstring
xstrip () const {
624 if (len
== 0) return res
;
625 if (opIndex(0) > ' ' && opIndex(len
-1) > ' ') { res
= this; return res
; }
626 const(char)[] dt = getData().xstrip
;
631 // this can never return `int.min` from conversion!
632 int toInt (int defval
=int.min
) const pure {
633 const(char)[] dt = getData().xstrip
;
634 if (dt.length
== 0) return defval
;
636 if (dt[0] == '+') dt = dt[1..$];
637 else if (dt[0] == '-') { neg = true; dt = dt[1..$]; }
638 bool wasDigit
= false;
640 foreach (immutable char ch
; dt) {
641 if (ch
== '_' || ch
== ' ' || ch
== ',') continue;
642 immutable int dg
= digitInBase(ch
, 10);
643 if (dg
< 0) return defval
;
646 if (n
< 0) return defval
;
648 if (n
< 0) return defval
;
650 if (!wasDigit
) return defval
;
656 private static struct LineIteratorImpl
{
659 nothrow @trusted @nogc:
660 this (dynstring src
) { s
= src
; }
662 @property bool empty () const pure { return (curridx
>= s
.length
); }
664 @property auto save () { LineIteratorImpl res
= LineIteratorImpl(s
); res
.curridx
= curridx
; return res
; }
666 @property const(char)[] front () const pure {
667 if (empty
) return null;
668 const(char)[] ss
= s
.getData
[curridx
..$];
669 auto ep
= ss
.indexOf('\n');
670 if (ep
>= 0) ss
= ss
[0..ep
];
676 const(char)[] ss
= s
.getData
;
677 auto ep
= ss
.indexOf('\n', curridx
);
678 if (ep
< 0) { curridx
= s
.length
; return; }
683 auto byLine () { return LineIteratorImpl(this); }
687 version(dynstring_test
) {
694 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
);
696 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
);
698 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
);
700 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
);
702 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
);
705 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
);
707 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
);
709 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
);
710 writeln("tmp[2]='", tmp
[2], "'");
713 tmp
= tmp
.xstripright
;
714 writeln("tmp: <", tmp
.getData
, ">");
717 tmp
= tmp
.xstripleft
;
718 writeln("tmp: <", tmp
.getData
, ">");
722 writeln("tmp: <", tmp
.getData
, ">");