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;
27 // ////////////////////////////////////////////////////////////////////////// //
28 alias dynstr
= dynstring
;
30 public struct dynstring
{
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
; }
44 uint ofs
= 0, len
= 0; // for slices
46 nothrow @trusted @nogc:
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();
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
);
72 void incRef () const {
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
); }
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();
91 bool isMyData (const(void)* ptr
) const pure {
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 {
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;
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
) {
115 if (sz
> 0x3fff_ffffU
) outofmem();
117 // if empty, allocate new storage and data
119 import core
.stdc
.stdlib
: calloc
, malloc
, free
;
121 dp
= cast(Data
*)calloc(1, Data
.sizeof
);
122 if (dp
is null) outofmem();
124 sz
= alignSize
!overalloc(sz
);
125 void *np
= malloc(sz
);
126 if (np
is null) outofmem();
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
);
137 // if shared, allocate completely new data and storage
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
);
148 Data
* dpnew
= cast(Data
*)calloc(1, Data
.sizeof
);
149 if (dpnew
is null) outofmem();
151 void *np
= malloc(sz
);
152 if (np
is null) outofmem();
156 if (len
) dpnew
.ptr
[0..len
] = dp
.ptr
[ofs
..ofs
+len
];
158 udata
= cast(usize
)dpnew
;
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
);
167 // if unshared slice, normalise storage
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
);
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
);
179 // not shared, not slice, expand storage
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
);
185 if (sz
> dp
.alloted
) {
186 import core
.stdc
.stdlib
: realloc
;
187 void *np
= realloc(dp
.ptr
, sz
);
188 if (np
is null) outofmem();
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
) = `
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));
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
) {
217 ensureRoomFor
!false(s
.length
);
218 datap
.ptr
[0..s
.length
] = s
[];
219 len
= datap
.used
= cast(uint)s
.length
;
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; }
236 if (atomicLoad(dp
.rc
) == 1) {
239 import core
.stdc
.string
: memmove
;
240 if (len
) memmove(dp
.ptr
, dp
.ptr
+ofs
, len
);
243 if (len
!= dp
.used
) { assert(len
<= dp
.used
); dp
.used
= len
; }
244 static if (compress
) {
245 if ((dp
.used|
0x1fU
)+1U < dp
.alloted
) {
247 import core
.stdc
.stdlib
: realloc
;
248 immutable uint sz
= (dp
.used|
0x1fU
)+1U;
249 void *np
= realloc(dp
.ptr
, 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));
262 // allocate new storage
264 tmpstr
.set(getData());
266 udata
= tmpstr
.udata
;
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
);
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; }
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
;
330 udata
= tmpstr
.udata
;
334 tmpstr
.ofs
= tmpstr
.len
= 0;
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);
352 } else if (newlen
!= len
) {
353 ensureRoomFor
!false(newlen
-len
);
354 assert(len
< newlen
);
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
) {
381 // same data, so RC is already valid
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);
415 res
.set(other
.getData
);
416 } else if (!other
.len
) {
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
;
429 dynstring
opBinary(string op
:"~") (const(char)[] other
) const {
430 pragma(inline
, true);
434 } else if (!other
.length
) {
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
;
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");
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)");
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 {
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; }
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
!= '?') {
502 append(hexd
[(cast(ubyte)ch
)>>4]);
503 append(hexd
[(cast(ubyte)ch
)&0x0f]);
510 void appendB64Encoded (const(void)[] buf
, uint maxlinelen
=76) {
511 static immutable string b64alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
516 void putB64Char (immutable char ch
) nothrow @trusted @nogc {
517 if (maxlinelen
&& linelen
>= maxlinelen
) { append("\r\n"); linelen
= 0; }
522 void encodeChunk () nothrow @trusted @nogc {
523 if (btspos
== 0) return;
524 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0xfc)>>2]);
526 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0x03)<<4]);
527 /*static if (padding)*/ { putB64Char('='); putB64Char('='); }
530 putB64Char(b64alphabet
.ptr
[((bts.ptr
[0]&0x03)<<4)|
((bts.ptr
[1]&0xf0)>>4)]);
532 putB64Char(b64alphabet
.ptr
[(bts.ptr
[1]&0x0f)<<2]);
533 /*static if (padding)*/ putB64Char('=');
536 putB64Char(b64alphabet
.ptr
[((bts.ptr
[1]&0x0f)<<2)|
((bts.ptr
[2]&0xc0)>>6)]);
537 putB64Char(b64alphabet
.ptr
[bts.ptr
[2]&0x3f]);
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) {
558 static if (__traits(isUnsigned
, T
)) {
559 auto len
= snprintf(buf
.ptr
, buf
.length
, "%u", cast(uint)v
);
561 auto len
= snprintf(buf
.ptr
, buf
.length
, "%d", cast(int)v
);
565 char[128] buf
= void;
566 static if (__traits(isUnsigned
, T
)) {
567 auto len
= snprintf(buf
.ptr
, buf
.length
, "%llu", cast(ulong)v
);
569 auto len
= snprintf(buf
.ptr
, buf
.length
, "%lld", cast(long)v
);
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;
586 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
587 foreach (ref char ch
; buf
) if (isCtrl(ch
)) ch
= repch
;
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;
597 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
598 foreach (ref char ch
; buf
) if (ch
>= 'A' && ch
<= 'Z') ch
+= 32;
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;
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 {
614 if (len
== 0) return res
;
615 if (opIndex(len
-1) > ' ') { res
= this; return res
; }
616 const(char)[] dt = getData().xstripright
;
621 dynstring
xstripleft () const {
623 if (len
== 0) return res
;
624 if (opIndex(0) > ' ') { res
= this; return res
; }
625 const(char)[] dt = getData().xstripleft
;
630 dynstring
xstrip () const {
632 if (len
== 0) return res
;
633 if (opIndex(0) > ' ' && opIndex(len
-1) > ' ') { res
= this; return res
; }
634 const(char)[] dt = getData().xstrip
;
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
;
644 if (dt[0] == '+') dt = dt[1..$];
645 else if (dt[0] == '-') { neg = true; dt = dt[1..$]; }
646 bool wasDigit
= false;
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
;
654 if (n
< 0) return defval
;
656 if (n
< 0) return defval
;
658 if (!wasDigit
) return defval
;
665 version(dynstring_test
) {
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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], "'");
691 tmp
= tmp
.xstripright
;
692 writeln("tmp: <", tmp
.getData
, ">");
695 tmp
= tmp
.xstripleft
;
696 writeln("tmp: <", tmp
.getData
, ">");
700 writeln("tmp: <", tmp
.getData
, ">");