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
;
23 private import iv
.utfutil
;
25 //version = dynstring_debug;
26 //version = dynstring_debug_clear;
27 //version = dynstring_more_asserts;
30 // ////////////////////////////////////////////////////////////////////////// //
31 alias dynstr
= dynstring
;
33 public struct dynstring
{
40 // string data follows
42 inout(char)* ptr () inout pure nothrow @trusted @nogc { pragma(inline
, true); return (cast(inout(char)*)&this)+Data
.sizeof
; }
44 static assert(Data
.sizeof
== 16, "invalid `dynstring.Data` size");
48 uint ofs
= 0, len
= 0; // for slices
50 nothrow @trusted @nogc:
52 inout(Data
)* datap () inout pure { pragma(inline
, true); return cast(Data
*)udata
; }
54 void outofmem () const {
55 import core
.exception
: onOutOfMemoryErrorNoGC
;
56 version(dynstring_test
) assert(0, "out of memory");
57 onOutOfMemoryErrorNoGC();
60 void incRef () const {
63 atomicFetchAdd((cast(Data
*)udata
).rc
, 1);
64 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
); }
71 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
); }
72 if (atomicFetchSub((cast(Data
*)udata
).rc
, 1) == 1) {
73 version(dynstring_debug
) {
74 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
);
76 import core
.stdc
.stdlib
: free
;
77 version(dynstring_debug_clear
) import core
.stdc
.string
: memset
;
78 version(dynstring_debug_clear
) assert((cast(Data
*)udata
).used
<= (cast(Data
*)udata
).alloted
);
79 version(dynstring_debug_clear
) memset((cast(Data
*)udata
), 0, Data
.sizeof
);
80 free((cast(Data
*)udata
));
88 bool isMyData (const(void)* ptr
) const pure {
90 if (ptr
is null ||
!udata
) return false;
91 immutable usize cc
= cast(usize
)ptr
;
92 return (cc
>= udata
+Data
.sizeof
&& cc
< udata
+Data
.sizeof
+datap
.alloted
);
95 static uint alignSize(bool overalloc
) (uint sz
) pure {
97 static if (overalloc
) {
98 if (sz
<= 256) return (sz|
0x1fU
)+1U;
99 else if (sz
<= 1024) return (sz|
0x3fU
)+1U;
100 else if (sz
<= 8192) return (sz|
0x1ffU
)+1U;
101 else if (sz
<= 16384) return (sz|
0x3ffU
)+1U;
102 else if (sz
<= 32768) return (sz|
0xfffU
)+1U;
103 else return (sz|
0x1fffU
)+1U;
105 return (sz|
0x1fU
)+1U;
109 // make sure that we have a room for at least `sz` chars
110 void ensureRoomFor(bool overalloc
) (uint sz
) {
112 if (sz
> 0x3fff_ffffU
) outofmem();
113 // if empty, allocate new storage and data
115 import core
.stdc
.stdlib
: malloc
;
116 version(dynstring_more_asserts
) assert(len
== 0);
117 sz
= alignSize
!overalloc(sz
);
118 Data
* xdp
= cast(Data
*)malloc(Data
.sizeof
+sz
);
119 if (xdp
is null) outofmem();
124 udata
= cast(usize
)xdp
;
125 ofs
= len
= 0; // just in case
126 version(dynstring_debug
) {
127 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",
128 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
132 // if shared, allocate completely new data and storage
134 if (atomicLoad(dp
.rc
) != 1) {
135 version(dynstring_debug
) {
136 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",
137 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
139 import core
.stdc
.stdlib
: malloc
;
140 if (len
> 0x3fff_ffffU ||
uint.max
-len
<= sz || len
+sz
> 0x3fff_ffffU
) outofmem();
141 sz
= alignSize
!overalloc(len
+sz
);
142 version(dynstring_more_asserts
) assert(sz
> len
);
143 Data
* dpnew
= cast(Data
*)malloc(Data
.sizeof
+sz
);
144 if (dpnew
is null) outofmem();
149 if (len
) dpnew
.ptr
[0..len
] = dp
.ptr
[ofs
..ofs
+len
];
151 udata
= cast(usize
)dpnew
;
152 ofs
= 0; // always reset by `decRef()`, but meh...
153 len
= dpnew
.used
; // it was reset by `decRef()`, so restore it
154 version(dynstring_debug
) {
155 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",
156 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
160 // if unshared slice, normalise storage
162 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
); }
163 import core
.stdc
.string
: memmove
;
164 if (len
) memmove(dp
.ptr
, dp
.ptr
+ofs
, len
);
167 if (len
!= dp
.used
) {
168 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
); }
169 version(dynstring_more_asserts
) assert(len
<= dp
.used
);
172 // not shared, not slice, expand storage
173 version(dynstring_more_asserts
) assert(ofs
== 0);
174 version(dynstring_more_asserts
) assert(len
== dp
.used
);
175 if (len
> 0x3fff_ffffU ||
uint.max
-len
<= sz || len
+sz
> 0x3fff_ffffU
) outofmem();
176 sz
= alignSize
!overalloc(len
+sz
);
177 version(dynstring_more_asserts
) assert(sz
> len
);
178 if (sz
> dp
.alloted
) {
179 import core
.stdc
.stdlib
: realloc
;
180 Data
* np
= cast(Data
*)realloc(dp
, Data
.sizeof
+sz
);
181 if (np
is null) outofmem();
183 udata
= cast(usize
)np
;
184 version(dynstring_debug
) {
185 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",
186 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0), sz
);
191 enum MixinEnsureUniqueBuf(string bname
) = `
193 if (isMyData(`~bname
~`.ptr)) {
194 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",
195 cast(uint)udata, datap.rc, ofs, len, datap.used, datap.alloted, cast(uint)`~bname
~`.ptr); }
196 tmpstr.set(`~bname
~`);
197 `~bname
~` = cast(typeof(`~bname
~`))tmpstr.getData();
198 version(dynstring_more_asserts) assert(!isMyData(`~bname
~`.ptr));
206 this() (in auto ref dynstring s
) { pragma(inline
, true); if (s
.len
) { udata
= s
.udata
; ofs
= s
.ofs
; len
= s
.len
; incRef(); } }
208 this (const(char)[] s
) {
210 ensureRoomFor
!false(s
.length
);
211 datap
.ptr
[0..s
.length
] = s
[];
212 len
= datap
.used
= cast(uint)s
.length
;
217 this (in char ch
) { ensureRoomFor
!false(1); datap
.ptr
[0] = ch
; len
= datap
.used
= 1; ofs
= 0; }
219 this (this) { pragma(inline
, true); if (udata
) incRef(); }
220 ~this () { pragma(inline
, true); if (udata
) decRef(); }
222 void clear () { pragma(inline
, true); decRef(); }
224 void makeUnique(bool compress
=false) () {
225 if (!udata
) { ofs
= len
= 0; return; }
226 if (!len
) { decRef(); return; }
229 if (atomicLoad(dp
.rc
) == 1) {
232 import core
.stdc
.string
: memmove
;
233 if (len
) memmove(dp
.ptr
, dp
.ptr
+ofs
, len
);
236 if (len
!= dp
.used
) { version(dynstring_more_asserts
) assert(len
<= dp
.used
); dp
.used
= len
; }
237 static if (compress
) {
238 if ((dp
.used|
0x1fU
)+1U < dp
.alloted
) {
240 import core
.stdc
.stdlib
: realloc
;
241 immutable uint sz
= (dp
.used|
0x1fU
)+1U;
242 Data
* np
= cast(Data
*)realloc(dp
, Data
.sizeof
+sz
);
245 udata
= cast(usize
)np
;
246 version(dynstring_debug
) {
247 import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "UNIQUERE: udata=0x%08x; rc=%u; ofs=%u; len=%u; used=%u; alloted=%u\n",
248 cast(uint)udata
, (udata ? datap
.rc
: 0), ofs
, len
, (udata ? datap
.used
: 0), (udata ? datap
.alloted
: 0));
255 // allocate new storage
257 tmpstr
.set(getData());
259 udata
= tmpstr
.udata
;
263 tmpstr
.ofs
= tmpstr
.len
= 0;
266 //WARNING! MAKE SURE THAT YOU KNOW WHAT YOU'RE DOING!
267 char *makeUniquePointer () { pragma(inline
, true); makeUnique(); return (len ? datap
.ptr
: null); }
269 bool isSlice () const pure { pragma(inline
, true); return !!ofs
; }
270 bool isShared () const pure { pragma(inline
, true); return (udata
&& atomicLoad(datap
.rc
) != 1); }
271 uint capacity () const pure { pragma(inline
, true); return (udata ? datap
.alloted
: 0); }
273 void reserve(bool overalloc
=false) (uint newsz
) {
274 pragma(inline
, true);
275 if (newsz
&& newsz
< 0x3fff_ffffU
&& newsz
> len
) ensureRoomFor
!overalloc(newsz
-len
);
278 void append(bool overalloc
=true) (const(void)[] buf
) {
279 if (buf
.length
== 0) return;
280 if (buf
.length
> cast(usize
)0x3fff_ffff
) outofmem();
281 //if (udata && cast(usize)0x3000_0000-datap.used < buf.length) outofmem();
282 version(dynstring_debug
) {
283 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",
284 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
);
286 mixin(MixinEnsureUniqueBuf
!"buf");
287 version(dynstring_debug
) {
288 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",
289 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
);
291 ensureRoomFor
!overalloc(buf
.length
);
292 version(dynstring_debug
) {
293 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",
294 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
);
296 version(dynstring_more_asserts
) assert(ofs
== 0);
298 version(dynstring_more_asserts
) assert(data
!is null && data
.used
+buf
.length
<= data
.alloted
);
299 version(dynstring_more_asserts
) assert(len
== datap
.used
);
300 data
.ptr
[len
..len
+buf
.length
] = cast(const(char)[])buf
;
301 data
.used
+= cast(uint)buf
.length
;
302 len
+= cast(uint)buf
.length
;
303 version(dynstring_more_asserts
) assert(len
<= data
.used
);
305 void append(bool overalloc
=true) (in char ch
) { pragma(inline
, true); append
!overalloc((&ch
)[0..1]); }
307 void set (const(void)[] buf
) {
308 if (buf
.length
> cast(usize
)0x3fff_ffff
) outofmem();
309 if (buf
.length
== 0) { decRef(); return; }
311 if (isMyData(buf
.ptr
)) {
312 // do not copy data if it is not necessary
313 if (ofs
== 0 && buf
.ptr
== datap
.ptr
&& buf
.length
<= datap
.used
) {
314 if (len
!= cast(uint)buf
.length
) {
315 len
= cast(uint)buf
.length
;
316 if (atomicLoad(datap
.rc
) == 1) datap
.used
= len
;
323 udata
= tmpstr
.udata
;
327 tmpstr
.ofs
= tmpstr
.len
= 0;
332 version(dynstring_more_asserts
) assert(udata
== 0 && ofs
== 0 && len
== 0);
333 ensureRoomFor
!false(buf
.length
);
334 datap
.ptr
[0..buf
.length
] = cast(const(char)[])buf
;
335 len
= datap
.used
= cast(uint)buf
.length
;
337 void set (in char ch
) { pragma(inline
, true); set((&ch
)[0..1]); }
339 uint length () const pure { pragma(inline
, true); return len
; }
341 void length (in uint newlen
) {
342 pragma(inline
, true);
345 } else if (newlen
!= len
) {
346 ensureRoomFor
!false(newlen
-len
);
347 version(dynstring_more_asserts
) assert(len
< newlen
);
348 version(dynstring_more_asserts
) assert(ofs
== 0);
349 version(dynstring_more_asserts
) assert(newlen
<= datap
.alloted
);
350 datap
.ptr
[len
..newlen
] = 0;
351 len
= datap
.used
= newlen
;
355 const(char)[] getData () const pure { pragma(inline
, true); return (len ? datap
.ptr
[ofs
..ofs
+len
] : null); }
356 const(ubyte)[] getBytes () const pure { pragma(inline
, true); return cast(const(ubyte)[])(len ? datap
.ptr
[ofs
..ofs
+len
] : null); }
358 void opAssign() (const(char)[] s
) { pragma(inline
, true); set(s
); }
359 void opAssign() (in char ch
) { pragma(inline
, true); set(ch
); }
361 void opAssign() (in auto ref dynstring s
) {
362 pragma(inline
, true);
363 if (s
.udata
!= udata
) {
374 // same data, so RC is already valid
380 void opOpAssign(string op
:"~") (const(char)[] s
) { pragma(inline
, true); if (s
.length
) append(s
); }
381 void opOpAssign(string op
:"~") (in char ch
) { pragma(inline
, true); append(ch
); }
382 void opOpAssign(string op
:"~") (in auto ref dynstring s
) { pragma(inline
, true); if (s
.len
) append(s
.getData()); }
384 const(char)[] opCast(T
=const(char)[]) () const pure { pragma(inline
, true); return getData(); }
385 const(ubyte)[] opCast(T
=const(ubyte)[]) () const pure { pragma(inline
, true); return getBytes(); }
387 bool opCast(T
=bool) () const pure { pragma(inline
, true); return (len
!= 0); }
389 bool opEqual (const(char)[] other
) const pure { pragma(inline
, true); return (other
.length
== len
&& other
[] == getBytes()[]); }
390 bool opEqual() (in auto ref dynstring other
) const pure { pragma(inline
, true); return (other
.len
== len
&& other
.getBytes()[] == getBytes()[]); }
392 int opCmp (const(char)[] other
) const pure {
393 if (len
== 0) return (other
.length ?
-1 : 0);
394 if (other
.length
== 0) return 1;
395 if (len
== other
.length
&& datap
.ptr
+ofs
== other
.ptr
) return 0;
396 import core
.stdc
.string
: memcmp
;
397 immutable int cres
= memcmp(datap
.ptr
+ofs
, other
.ptr
, (len
<= other
.length ? len
: other
.length
));
398 if (cres
!= 0) return (cres
< 0 ?
-1 : +1);
399 return (len
< other
.length ?
-1 : len
> other
.length ?
+1 : 0);
402 int opCmp() (in auto ref dynstring other
) const { pragma(inline
, true); return opCmp(other
.getData
); }
404 dynstring
opBinary(string op
:"~") (in auto ref dynstring other
) const {
405 pragma(inline
, true);
408 res
.set(other
.getData
);
409 } else if (!other
.len
) {
412 if (uint.max
-len
<= other
.len
) outofmem();
413 res
.ensureRoomFor
!false(len
+other
.len
);
414 res
.datap
.ptr
[0..len
] = getData();
415 res
.datap
.ptr
[len
..len
+other
.len
] = other
.getData();
416 version(dynstring_more_asserts
) assert(res
.ofs
== 0);
417 res
.datap
.used
= res
.len
= len
+other
.len
;
422 dynstring
opBinary(string op
:"~") (const(char)[] other
) const {
423 pragma(inline
, true);
427 } else if (!other
.length
) {
430 if (uint.max
-len
<= other
.length
) outofmem();
431 res
.ensureRoomFor
!false(len
+cast(uint)other
.length
);
432 res
.datap
.ptr
[0..len
] = getData();
433 res
.datap
.ptr
[len
..len
+other
.length
] = other
;
434 version(dynstring_more_asserts
) assert(res
.ofs
== 0);
435 res
.datap
.used
= res
.len
= len
+cast(uint)other
.length
;
440 char opIndex (in uint pos
) const pure {
441 pragma(inline
, true);
442 //if (pos >= len) assert(0, "dynstring index out of bounds"); return datap.ptr[ofs+pos];
443 return (pos
< len ? datap
.ptr
[ofs
+pos
] : 0);
446 void opIndexAssign (in char ch
, in uint pos
) {
447 pragma(inline
, true);
448 if (pos
>= len
) assert(0, "dynstring index out of bounds");
450 version(dynstring_more_asserts
) assert(pos
< len
);
451 datap
.ptr
[ofs
+pos
] = ch
;
454 dynstring
opSlice (in uint lo
, in uint hi
) const {
456 if (lo > hi) assert(0, "dynstring slice index out of bounds (0)");
457 if (lo > len || hi > len) assert(0, "dynstring slice index out of bounds (1)");
460 if (lo
> hi || lo
> len || hi
> len
) return res
;
470 uint opDollar () const pure { pragma(inline
, true); return len
; }
472 void appendQEncoded (const(void)[] ss
) {
473 static bool isSpecial (immutable char ch
) pure nothrow @safe @nogc {
484 if (ss
.length
== 0) return;
485 const(char)[] s
= cast(const(char)[])ss
;
487 static immutable string hexd
= "0123456789abcdef";
489 bool needWork
= (s
[0] == '=' || s
[0] == '?');
490 if (!needWork
) foreach (char ch
; s
) if (isSpecial(ch
)) { needWork
= true; break; }
495 mixin(MixinEnsureUniqueBuf
!"ss");
496 append("=?UTF-8?Q?"); // quoted printable
497 foreach (char ch
; s
) {
498 if (ch
<= ' ') ch
= '_';
499 if (!isSpecial(ch
) && ch
!= '=' && ch
!= '?') {
503 append(hexd
[(cast(ubyte)ch
)>>4]);
504 append(hexd
[(cast(ubyte)ch
)&0x0f]);
511 void appendB64Encoded (const(void)[] buf
, uint maxlinelen
=76) {
512 static immutable string b64alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
517 void putB64Char (immutable char ch
) nothrow @trusted @nogc {
518 if (maxlinelen
&& linelen
>= maxlinelen
) { append("\r\n"); linelen
= 0; }
523 void encodeChunk () nothrow @trusted @nogc {
524 if (btspos
== 0) return;
525 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0xfc)>>2]);
527 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0x03)<<4]);
528 /*static if (padding)*/ { putB64Char('='); putB64Char('='); }
531 putB64Char(b64alphabet
.ptr
[((bts.ptr
[0]&0x03)<<4)|
((bts.ptr
[1]&0xf0)>>4)]);
533 putB64Char(b64alphabet
.ptr
[(bts.ptr
[1]&0x0f)<<2]);
534 /*static if (padding)*/ putB64Char('=');
537 putB64Char(b64alphabet
.ptr
[((bts.ptr
[1]&0x0f)<<2)|
((bts.ptr
[2]&0xc0)>>6)]);
538 putB64Char(b64alphabet
.ptr
[bts.ptr
[2]&0x3f]);
544 mixin(MixinEnsureUniqueBuf
!"buf");
546 foreach (immutable ubyte ib
; (cast(const(ubyte)[])buf
)[]) {
547 bts.ptr
[btspos
++] = ib
;
548 if (btspos
== 3) encodeChunk();
550 if (btspos
!= 0) encodeChunk();
552 if (maxlinelen
) append("\r\n");
555 void appendNum(T
) (in T v
) if (__traits(isIntegral
, T
)) {
556 import core
.stdc
.stdio
: snprintf
;
557 static if (T
.sizeof
<= 4) {
559 static if (__traits(isUnsigned
, T
)) {
560 auto len
= snprintf(buf
.ptr
, buf
.length
, "%u", cast(uint)v
);
562 auto len
= snprintf(buf
.ptr
, buf
.length
, "%d", cast(int)v
);
566 char[128] buf
= void;
567 static if (__traits(isUnsigned
, T
)) {
568 auto len
= snprintf(buf
.ptr
, buf
.length
, "%llu", cast(ulong)v
);
570 auto len
= snprintf(buf
.ptr
, buf
.length
, "%lld", cast(long)v
);
576 void removeASCIICtrls (in char repch
=' ') {
577 static bool isCtrl (in char ch
) pure nothrow @safe @nogc {
578 pragma(inline
, true);
579 return (ch
< 32 && ch
!= '\t' && ch
!= '\n');
582 bool needWork
= false;
583 foreach (immutable char ch
; getData
) if (isCtrl(ch
)) { needWork
= true; break; }
584 if (!needWork
) return;
587 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
588 foreach (ref char ch
; buf
) if (isCtrl(ch
)) ch
= repch
;
592 void lowerInPlace () {
593 bool needWork
= false;
594 foreach (immutable char ch
; getData
) if (ch
>= 'A' && ch
<= 'Z') { needWork
= true; break; }
595 if (!needWork
) return;
598 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
599 foreach (ref char ch
; buf
) if (ch
>= 'A' && ch
<= 'Z') ch
+= 32;
603 void upperInPlace () {
604 bool needWork
= false;
605 foreach (immutable char ch
; getData
) if (ch
>= 'a' && ch
<= 'z') { needWork
= true; break; }
606 if (!needWork
) return;
609 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
610 foreach (ref char ch
; buf
) if (ch
>= 'a' && ch
<= 'z') ch
-= 32;
613 dynstring
xstripright () const {
615 if (len
== 0) return res
;
616 if (opIndex(len
-1) > ' ') { res
= this; return res
; }
617 const(char)[] dt = getData().xstripright
;
622 dynstring
xstripleft () const {
624 if (len
== 0) return res
;
625 if (opIndex(0) > ' ') { res
= this; return res
; }
626 const(char)[] dt = getData().xstripleft
;
631 dynstring
xstrip () const {
633 if (len
== 0) return res
;
634 if (opIndex(0) > ' ' && opIndex(len
-1) > ' ') { res
= this; return res
; }
635 const(char)[] dt = getData().xstrip
;
640 // this can never return `int.min` from conversion!
641 int toInt (int defval
=int.min
) const pure {
642 const(char)[] dt = getData().xstrip
;
643 if (dt.length
== 0) return defval
;
645 if (dt[0] == '+') dt = dt[1..$];
646 else if (dt[0] == '-') { neg = true; dt = dt[1..$]; }
647 bool wasDigit
= false;
649 foreach (immutable char ch
; dt) {
650 if (ch
== '_' || ch
== ' ' || ch
== ',') continue;
651 immutable int dg
= digitInBase(ch
, 10);
652 if (dg
< 0) return defval
;
655 if (n
< 0) return defval
;
657 if (n
< 0) return defval
;
659 if (!wasDigit
) return defval
;
665 private static struct LineIteratorImpl
{
669 nothrow @trusted @nogc:
670 this (dynstring src
) { s
= src
; }
671 this (in ref LineIteratorImpl src
) { s
= src
.s
; curridx
= src
.curridx
; }
673 @property bool empty () const pure { return (curridx
>= s
.length
); }
675 @property auto save () { return LineIteratorImpl(s
); }
677 @property const(char)[] front () const pure {
678 if (empty
) return null;
679 const(char)[] ss
= s
.getData
[curridx
..$];
680 auto ep
= ss
.indexOf('\n');
681 if (ep
>= 0) ss
= ss
[0..ep
];
687 const(char)[] ss
= s
.getData
;
688 auto ep
= ss
.indexOf('\n', curridx
);
689 if (ep
< 0) { curridx
= s
.length
; return; }
694 auto byLine () { return LineIteratorImpl(this); }
697 private static struct UniCharIteratorImpl
{
701 nothrow @trusted @nogc:
702 this (dynstring src
) { s
= src
; }
703 this (in ref UniCharIteratorImpl src
) { s
= src
.s
; curridx
= src
.curridx
; }
705 @property bool empty () const pure { return (curridx
>= s
.length
); }
707 @property auto save () { return UniCharIteratorImpl(s
); }
709 @property dchar front () const {
710 if (empty
) return Utf8DecoderFast
.replacement
;
713 while (pos
< s
.length
) if (dc
.decodeSafe(s
[pos
++])) return dc
.codepoint
;
714 return Utf8DecoderFast
.replacement
;
720 while (curridx
< s
.length
) if (dc
.decodeSafe(s
[curridx
++])) break;
724 auto byUniChar () { return UniCharIteratorImpl(this); }
728 version(dynstring_test
) {
735 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
);
737 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
);
739 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
);
741 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
);
743 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
);
746 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
);
748 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
);
750 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
);
751 writeln("tmp[2]='", tmp
[2], "'");
754 tmp
= tmp
.xstripright
;
755 writeln("tmp: <", tmp
.getData
, ">");
758 tmp
= tmp
.xstripleft
;
759 writeln("tmp: <", tmp
.getData
, ">");
763 writeln("tmp: <", tmp
.getData
, ">");
766 foreach (dchar dch
; tmp
.byUniChar
) writefln("0x%04x", cast(uint)dch
);