1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // some string operations: quoting, `indexOf()` for non-utf8
18 module iv
.strex
/*is aliced*/;
21 /// quote string: append double quotes, screen all special chars;
22 /// so quoted string forms valid D string literal.
24 string
quote (const(char)[] s
) {
25 import std
.array
: appender
;
26 import std
.format
: formatElement
, FormatSpec
;
27 auto res
= appender
!string();
28 FormatSpec
!char fspc
; // defaults to 's'
29 formatElement(res
, s
, fspc
);
34 /// convert integral number to number with commas
35 char[] intWithCommas(T
) (char[] dest
, T nn
, char comma
=',') if (__traits(isIntegral
, T
)) {
36 static if (__traits(isUnsigned
, T
)) {
39 static if (T
.sizeof
< 8) {
46 static if (T
.sizeof
< 8) {
54 if (n
< 0) n
= T
.max
; //FIXME
58 int bpos
= cast(int)buf
.length
;
61 if (leftToComma
-- == 0) { buf
[--bpos
] = comma
; leftToComma
= 2; }
62 buf
[--bpos
] = cast(char)('0'+n
%10);
63 } while ((n
/= 10) != 0);
64 if (neg) buf
[--bpos
] = '-';
65 auto len
= buf
.length
-bpos
;
66 if (dest
is null) dest
= new char[](len
);
67 if (len
> dest
.length
) len
= dest
.length
;
68 dest
[0..len
] = buf
[bpos
..bpos
+len
];
72 char[] intWithCommas(T
) (T nn
, char comma
=',') if (__traits(isIntegral
, T
)) { return intWithCommas(null, nn
, comma
); }
75 //char tolower (char ch) pure nothrow @trusted @nogc { pragma(inline, true); return (ch >= 'A' && ch <= 'Z' ? cast(char)(ch-'A'+'a') : ch); }
76 //char toupper (char ch) pure nothrow @trusted @nogc { pragma(inline, true); return (ch >= 'a' && ch <= 'z' ? cast(char)(ch-'a'+'A') : ch); }
77 char tolower (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return cast(char)(ch
+((ch
>= 'A' && ch
<= 'Z')<<5)); }
78 char toupper (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return cast(char)(ch
-((ch
>= 'a' && ch
<= 'z')<<5)); }
80 bool islower (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ch
>= 'a' && ch
<= 'z'); }
81 bool isupper (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ch
>= 'A' && ch
<= 'Z'); }
83 bool isalpha (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return ((ch
>= 'A' && ch
<= 'Z') ||
(ch
>= 'a' && ch
<= 'z')); }
84 bool isdigit (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return (ch
>= '0' && ch
<= '9'); }
85 bool isalnum (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return ((ch
>= 'A' && ch
<= 'Z') ||
(ch
>= 'a' && ch
<= 'z') ||
(ch
>= '0' && ch
<= '9')); }
86 bool isxdigit (char ch
) pure nothrow @trusted @nogc { pragma(inline
, true); return ((ch
>= 'A' && ch
<= 'F') ||
(ch
>= 'a' && ch
<= 'f') ||
(ch
>= '0' && ch
<= '9')); }
88 /// case-insensitive char compare for ASCII
89 bool charEquCI (const char c0
, const char c1
) pure nothrow @trusted @nogc {
91 // (c0 |= 0x20) is lowercase-conversion for ASCII
92 // the good thing is that only uppercase letters will become lowercase letters,
93 // other things will become a garbage
94 // also, let's hope that any decent compiler is able to perform CSE here
96 c0
== c1 ||
// try the easiest case first
97 ((c0|
0x20) >= 'a' && (c0|
0x20) <= 'z' && // it wasn't a letter, no need to check the second char
98 (c0|
0x20) == (c1|
0x20)); // c1 will become a lowercase ascii only if it was uppercase/lowercase ascii
101 int digitInBase (const char ch
, const int base
=10) pure nothrow @trusted @nogc {
102 pragma(inline
, true);
104 ch
>= '0' && ch
<= '9' && ch
-'0' < base ? ch
-'0' :
105 base
> 10 && ch
>= 'A' && ch
< 'Z' && ch
-'A'+10 < base ? ch
-'A'+10 :
106 base
> 10 && ch
>= 'a' && ch
< 'z' && ch
-'a'+10 < base ? ch
-'a'+10 :
111 alias atof
= atofd
!float; /// very simple atof/atod converter. accepts exponents. returns NaN on error.
112 alias atod
= atofd
!double; /// very simple atof/atod converter. accepts exponents. returns NaN on error.
114 /// very simple atof/atod converter. accepts exponents.
115 /// returns NaN on error.
116 T
atofd(T
) (const(char)[] str) pure nothrow @trusted @nogc if (is(T
== float) ||
is(T
== double)) {
117 if (str.length
== 0) return T
.nan
; // oops
119 const(char)[] s
= str;
120 double res
= 0.0, sign
= 1.0;
121 bool hasIntPart
= false, hasFracPart
= false;
123 char peekChar () nothrow @trusted @nogc { pragma(inline
, true); return (s
.length ? s
.ptr
[0] : '\0'); }
124 void skipChar () nothrow @trusted @nogc { pragma(inline
, true); if (s
.length
> 0) s
= s
[1..$]; }
125 char getChar () nothrow @trusted @nogc { char ch
= 0; if (s
.length
> 0) { ch
= s
.ptr
[0]; s
= s
[1..$]; } return ch
; }
129 case '-': sign
= -1; goto case;
130 case '+': skipChar(); break;
135 if (isdigit(peekChar
)) {
137 while (isdigit(peekChar
)) res
= res
*10.0+(getChar()-'0');
141 if (peekChar
== '.') {
142 skipChar(); // skip '.'
143 if (isdigit(peekChar
)) {
147 while (isdigit(peekChar
)) {
149 num
= num
*10+(getChar()-'0');
151 res
+= cast(double)num
/divisor
;
155 // valid number should have integer or fractional part
156 if (!hasIntPart
&& !hasFracPart
) return T
.nan
;
159 if (peekChar
== 'e' || peekChar
== 'E') {
160 skipChar(); // skip 'E'
162 bool epositive
= true;
164 case '-': epositive
= false; goto case;
165 case '+': skipChar(); break;
169 while (isdigit(peekChar
)) expPart
= expPart
*10+(getChar()-'0');
171 foreach (immutable _
; 0..expPart
) res
*= 10.0;
173 foreach (immutable _
; 0..expPart
) res
/= 10.0;
177 return cast(T
)(res
*sign
);
182 bool strEquCI (const(char)[] s0
, const(char)[] s1
) pure nothrow @trusted @nogc {
183 if (s0
.length
!= s1
.length
) return false;
184 if (s0
.ptr
== s1
.ptr
) return true;
185 foreach (immutable idx
, char c0
; s0
) {
186 // try the easiest case first
188 if (c0
== s1
[idx
]) continue;
190 if (c0
== s1
.ptr
[idx
]) continue;
192 c0 |
= 0x20; // convert to ascii lowercase
193 if (c0
< 'a' || c0
> 'z') return false; // it wasn't a letter, no need to check the second char
194 // c0 is guaranteed to be a lowercase ascii here
196 if (c0
!= (s1
[idx
]|
0x20)) return false; // c1 will become a lowercase ascii only if it was uppercase/lowercase ascii
198 if (c0
!= (s1
.ptr
[idx
]|
0x20)) return false; // c1 will become a lowercase ascii only if it was uppercase/lowercase ascii
205 version(test_strex
) unittest {
206 assert(strEquCI("Alice", "alice"));
207 assert(strEquCI("alice", "Alice"));
208 assert(strEquCI("alice", "alice"));
213 int strCmpCI (const(char)[] s0
, const(char)[] s1
) pure nothrow @trusted @nogc {
214 auto slen
= s0
.length
;
215 if (s1
.length
== slen
&& s0
.ptr
== s1
.ptr
) return 0;
216 if (slen
> s1
.length
) slen
= s1
.length
;
218 foreach (immutable idx
, char c0
; s0
[0..slen
]) {
221 c1
= s1
[idx
].tolower
;
223 c1
= s1
.ptr
[idx
].tolower
;
225 if (c0
< c1
) return -1;
226 if (c0
> c1
) return 1;
228 if (s0
.length
< s1
.length
) return -1;
229 if (s0
.length
> s1
.length
) return +1;
234 inout(char)[] xstrip (inout(char)[] s
) pure nothrow @trusted @nogc {
236 while (s
.length
&& s
[0] <= ' ') s
= s
[1..$];
238 while (s
.length
&& s
.ptr
[0] <= ' ') s
= s
[1..$];
240 while (s
.length
&& s
[$-1] <= ' ') s
= s
[0..$-1];
245 inout(char)[] xstripleft (inout(char)[] s
) pure nothrow @trusted @nogc {
247 while (s
.length
&& s
[0] <= ' ') s
= s
[1..$];
249 while (s
.length
&& s
.ptr
[0] <= ' ') s
= s
[1..$];
255 inout(char)[] xstripright (inout(char)[] s
) pure nothrow @trusted @nogc {
256 while (s
.length
&& s
[$-1] <= ' ') s
= s
[0..$-1];
261 bool startsWith (const(char)[] str, const(char)[] pat
) pure nothrow @trusted @nogc {
262 if (pat
.length
> str.length
) return false;
263 return (str[0..pat
.length
] == pat
);
267 bool endsWith (const(char)[] str, const(char)[] pat
) pure nothrow @trusted @nogc {
268 if (pat
.length
> str.length
) return false;
269 return (str[$-pat
.length
..$] == pat
);
274 bool startsWithCI (const(char)[] str, const(char)[] pat
) pure nothrow @trusted @nogc {
275 if (pat
.length
> str.length
) return false;
276 return strEquCI(str[0..pat
.length
], pat
);
281 bool endsWithCI (const(char)[] str, const(char)[] pat
) pure nothrow @trusted @nogc {
282 if (pat
.length
> str.length
) return false;
283 return strEquCI(str[$-pat
.length
..$], pat
);
287 ptrdiff_t
indexOf (const(char)[] hay
, const(char)[] need
, size_t stIdx
=0) pure nothrow @trusted @nogc {
288 if (hay
.length
<= stIdx || need
.length
== 0 || need
.length
> hay
.length
-stIdx
) {
291 if (need
.length
== 1) {
293 return indexOf(hay
, need
[0], stIdx
);
295 return indexOf(hay
, need
.ptr
[0], stIdx
);
299 foreach (immutable idx
; stIdx
..hay
.length
-need
.length
+1) {
300 if (hay
[idx
..idx
+need
.length
] == need
) return idx
;
304 auto res
= cast(const(char)*)memmem(hay
.ptr
+stIdx
, hay
.length
-stIdx
, need
.ptr
, need
.length
);
305 return (res
!is null ?
cast(ptrdiff_t
)(res
-hay
.ptr
) : -1);
311 ptrdiff_t
indexOf (const(char)[] hay
, char ch
, size_t stIdx
=0) pure nothrow @trusted @nogc {
312 if (hay
.length
<= stIdx
) {
316 foreach (immutable idx
; stIdx
..hay
.length
) {
317 if (hay
[idx
] == ch
) return idx
;
321 import core
.stdc
.string
: memchr
;
322 auto res
= cast(const(char)*)memchr(hay
.ptr
+stIdx
, ch
, hay
.length
-stIdx
);
323 return (res
!is null ?
cast(ptrdiff_t
)(res
-hay
.ptr
) : -1);
329 ptrdiff_t
lastIndexOf (const(char)[] hay
, const(char)[] need
, size_t stIdx
=0) pure nothrow @trusted @nogc {
330 if (hay
.length
<= stIdx || need
.length
== 0 || need
.length
> hay
.length
-stIdx
) {
333 if (hay
.length
== 1) {
335 return lastIndexOf(hay
, need
[0], stIdx
);
337 return lastIndexOf(hay
, need
.ptr
[0], stIdx
);
341 foreach_reverse (immutable idx
; stIdx
..hay
.length
-need
.length
+1) {
342 if (hay
[idx
..idx
+need
.length
] == need
) return idx
;
346 auto res
= cast(char*)memrmem(hay
.ptr
+stIdx
, hay
.length
-stIdx
, need
.ptr
, need
.length
);
347 return (res
!is null ?
cast(ptrdiff_t
)(res
-hay
.ptr
) : -1);
353 ptrdiff_t
lastIndexOf (const(char)[] hay
, char ch
, size_t stIdx
=0) pure nothrow @trusted @nogc {
354 if (hay
.length
<= stIdx
) {
358 foreach_reverse (immutable idx
; stIdx
..hay
.length
) {
359 if (hay
[idx
] == ch
) return idx
;
363 auto res
= cast(const(char)*)memrchr(hay
.ptr
+stIdx
, ch
, hay
.length
-stIdx
);
364 return (res
!is null ?
cast(ptrdiff_t
)(res
-hay
.ptr
) : -1);
370 version(test_strex
) unittest {
371 assert(indexOf("Alice & Miriel", " & ") == 5);
372 assert(indexOf("Alice & Miriel", " &!") == -1);
373 assert(indexOf("Alice & Miriel", "Alice & Miriel was here!") == -1);
374 assert(indexOf("Alice & Miriel", '&') == 6);
376 assert(indexOf("Alice & Miriel", ch
) == 5);
378 assert(indexOf("Alice & Miriel", "i") == 2);
379 assert(indexOf("Alice & Miriel", "i", 6) == 9);
380 assert(indexOf("Alice & Miriel", "i", 12) == -1);
382 assert(indexOf("Alice & Miriel", "Miriel", 8) == 8);
383 assert(indexOf("Alice & Miriel", "Miriel", 9) == -1);
385 assert(lastIndexOf("Alice & Miriel", "i") == 11);
386 assert(lastIndexOf("Alice & Miriel", "i", 6) == 11);
387 assert(lastIndexOf("Alice & Miriel", "i", 11) == 11);
388 assert(lastIndexOf("Alice & Miriel", "i", 12) == -1);
390 assert(lastIndexOf("iiii", "ii") == 2);
394 string
detab (const(char)[] s
, uint tabSize
=8) {
397 import std
.array
: appender
;
398 auto res
= appender
!string();
401 foreach (char ch
; s
) {
402 if (ch
== '\n' || ch
== '\r') {
404 } else if (ch
== '\t') {
405 auto spins
= tabSize
-col
%tabSize
;
407 while (spins
-- > 1) res
.put(' ');
419 version(test_strex
) unittest {
420 assert(detab(" \n\tx", 9) == " \n x");
421 assert(detab(" ab\t asdf ") == " ab asdf ");
425 auto byLine(T
) (T s
) if (is(T
:const(char)[])) {
426 static struct Range(T
) {
431 this (T as
) { s
= as
; popFront(); }
433 @property bool empty () const { pragma(inline
, true); return (s
.length
== 0); }
434 @property T
front () const { pragma(inline
, true); return cast(T
)s
[0..llen
]; } // fuckin' const!
435 auto save () const @trusted { Range
!T res
= void; res
.s
= s
; res
.llen
= llen
; res
.npos
= npos
; return res
; }
436 void popFront () @trusted {
439 while (npos
< s
.length
) {
440 if (s
.ptr
[npos
] == '\r') {
442 if (s
.length
-npos
> 1 && s
.ptr
[npos
+1] == '\n') ++npos
;
446 if (s
.ptr
[npos
] == '\n') {
460 version(test_strex) unittest {
474 foreach (/+auto+/ line; s.byLine) {
476 writeln("LN: [", line, "]");
479 foreach (/+auto+/ line; ugly.byLine) {
481 writeln("LN: [", line, "]");
486 // string should be detabbed!
487 string
outdentAll (const(char)[] s
) {
488 import std
.array
: appender
;
489 // first calculate maximum indent spaces
490 uint maxspc
= uint.max
;
491 foreach (/*auto*/ line
; s
.byLine
) {
493 while (col
< line
.length
&& line
.ptr
[col
] <= ' ') {
494 if (line
.ptr
[col
] == '\t') assert(0, "can't outdent shit with tabs");
497 if (col
>= line
.length
) continue; // empty line, don't care
498 if (col
< maxspc
) maxspc
= col
;
499 if (col
== 0) break; // nothing to do anymore
502 auto res
= appender
!string();
503 foreach (/*auto*/ line
; s
.byLine
) {
505 while (col
< line
.length
&& line
.ptr
[col
] <= ' ') ++col
;
506 if (col
< line
.length
) {
508 res
.put(line
[maxspc
..$]);
517 version(test_strex
) unittest {
534 assert(pretty
== ugly
);
538 //From: Yahoo Groups <confirm-s2-2ny0qbq23nljzefbilh5vpjrg1pik5hf-ketmar=ketmar.no-ip.org@yahoogroups.com>
539 private bool isValidEmailNameChar (char ch
) pure nothrow @safe @nogc {
540 pragma(inline
, true);
541 if (ch
<= 32) return false;
542 if (ch
>= '0' && ch
<= '9') return true;
543 if (ch
>= 'a' && ch
<= 'z') ch
-= 32; // poor man's tolower
544 if (ch
>= 'A' && ch
<= 'Z') return true;
545 if (ch
== '_' || ch
== '+' || ch
== '-' || ch
== '=' || ch
== '.' || ch
== '$') return true;
546 if (ch
>= 128) return true; // why not?
548 if (ch
== '!' || ch
== '%' || ch
== '^' || ch
== '&' || ch
== '(' || ch
== ')') return true;
549 if (ch
== '?') return true;
554 private bool isValidEmailHostChar (char ch
) pure nothrow @safe @nogc {
555 pragma(inline
, true);
556 if (ch
<= 32 || ch
>= 127) return false;
557 if (ch
>= '0' && ch
<= '9') return true;
558 if (ch
>= 'a' && ch
<= 'z') ch
-= 32; // poor man's tolower
559 if (ch
>= 'A' && ch
<= 'Z') return true;
560 if (ch
== '-' || ch
== '.') return true;
565 bool isGoodEmail (const(char)[] s
) pure nothrow @trusted @nogc {
566 if (s
.length
== 0 || s
.ptr
[0] == '@') return false;
567 // parse part until '@'
570 if (ch
== '@') break;
571 if (!isValidEmailNameChar(ch
)) return false;
574 if (!s
.length
) return false; // no doggy
575 assert(s
.ptr
[0] == '@');
577 if (s
.length
== 0) return false;
580 if (!isValidEmailHostChar(ch
)) return false;
587 /// backslash in ranges is used to escaping; '<' and '>' matching word start and end
588 bool globmatch(bool casesens
=true) (const(char)[] str, const(char)[] pat
) pure nothrow @trusted @nogc {
589 static bool globIsWordChar (const char ch
) pure nothrow @safe @nogc {
590 pragma(inline
, true);
592 (ch
>= 'A' && ch
<= 'Z') ||
593 (ch
>= 'a' && ch
<= 'z') ||
594 (ch
>= '0' && ch
<= '9') ||
595 ch
== '_' || ch
>= 128;
598 // empty pattern cannot match non-empty string
599 if (pat
.length
== 0) return (str.length
== 0);
601 const(char)* realstart
= str.ptr
;
606 foreach (usize i
; 0..str.length
) {
607 static if (casesens
) {
608 immutable char sch
= str.ptr
[i
];
610 immutable char sch
= tolower(str.ptr
[i
]);
613 if (patpos
>= pat
.length
) goto starCheck
;
614 switch (pat
.ptr
[patpos
++]) {
615 case '?': // match anything
620 pat
= pat
[patpos
..$];
621 // skip excessive stars
622 while (pat
.length
&& pat
.ptr
[0] == '*') pat
= pat
[1..$];
623 if (pat
.length
== 0) return true;
627 bool hasMatch
= false;
628 bool inverted
= (pat
.ptr
[patpos
] == '^');
629 if (inverted
) ++patpos
;
630 if (patpos
>= pat
.length
) return false; // malformed pattern
632 char c0
= pat
.ptr
[patpos
++];
633 if (c0
== '\\' && patpos
< pat
.length
) c0
= pat
.ptr
[patpos
++];
634 if (patpos
>= pat
.length
) return false; // malformed pattern
635 static if (!casesens
) c0
= tolower(c0
);
637 if (pat
.ptr
[patpos
] == '-') {
639 ++patpos
; // skip '-'
640 if (patpos
>= pat
.length
) return false; // malformed pattern
641 c1
= pat
.ptr
[patpos
++];
642 if (c1
== '\\' && patpos
< pat
.length
) c1
= pat
.ptr
[patpos
++];
643 static if (!casesens
) c1
= tolower(c1
);
644 if (patpos
>= pat
.length
) return false; // malformed pattern
646 hasMatch
= (!hasMatch
&& sch
>= c0
&& sch
<= c1
);
647 } while (patpos
< pat
.length
&& pat
.ptr
[patpos
] != ']');
648 if (patpos
>= pat
.length || pat
.ptr
[patpos
] != ']') return false; // malformed pattern
650 if (inverted
) hasMatch
= !hasMatch
;
651 if (!hasMatch
) goto starCheck
;
654 case '<': // word boundary (start)
655 // current char must be a word char
656 if (!globIsWordChar(sch
)) goto starCheck
;
658 const usize realpos
= cast(ptrdiff_t
)(str.ptr
-realstart
)+i
;
659 // previous char must not be a word char
660 if (realpos
> 0 && globIsWordChar(realstart
[realpos
-1u])) goto starCheck
;
663 case '>': // word boundary (end)
664 // current char must not be a word char
665 if (globIsWordChar(sch
)) goto starCheck
;
667 const usize realpos
= cast(ptrdiff_t
)(str.ptr
-realstart
)+i
;
668 // previous char must be a word char
669 if (realpos
> 0 && !globIsWordChar(realstart
[realpos
-1u])) goto starCheck
;
674 if (patpos
>= pat
.length
) return false; // malformed pattern
677 static if (casesens
) {
678 if (sch
!= pat
.ptr
[patpos
-1]) goto starCheck
;
680 if (sch
!= tolower(pat
.ptr
[patpos
-1])) goto starCheck
;
685 pat
= pat
[patpos
..$];
686 // pattern may end with stars, skip them
687 // also skip word boundaries check (they are always true here)
688 while (pat
.length
&& (pat
.ptr
[0] == '*' || pat
.ptr
[0] == '<' || pat
.ptr
[0] == '>')) pat
= pat
[1..$];
689 return (pat
.length
== 0);
692 if (!star
) return false;
693 if (str.length
) str = str[1..$];
698 alias globmatchCI
= globmatch
!false;
701 pure nothrow @system @nogc:
703 extern(C
) inout(void)* memmem (inout(void)* haystack
, size_t haystacklen
, inout(void)* needle
, size_t needlelen
);
704 extern(C
) inout(void)* memrchr (inout(void)* s
, int ch
, size_t slen
);
706 inout(void)* memmem (inout(void)* haystack
, size_t haystacklen
, inout(void)* needle
, size_t needlelen
) {
707 // size_t is unsigned
708 if (needlelen
> haystacklen || needlelen
== 0) return null;
709 auto h
= cast(const(ubyte)*)haystack
;
710 auto n
= cast(const(ubyte)*)needle
;
711 foreach (immutable i
; 0..haystacklen
-needlelen
+1) {
712 import core
.stdc
.string
: memcmp
;
713 if (memcmp(h
+i
, n
, needlelen
) == 0) return cast(typeof(return))(h
+i
);
718 inout(void)* memrchr (inout(void)* haystack
, int ch
, size_t haystacklen
) {
719 // size_t is unsigned
720 if (haystacklen
== 0) return null;
721 auto h
= cast(const(ubyte)*)haystack
;
723 foreach_reverse (immutable idx
, ubyte v
; h
[0..haystacklen
]) {
724 if (v
== ch
) return cast(typeof(return))(h
+idx
);
730 inout(void)* memrmem (inout(void)* haystack
, size_t haystacklen
, inout(void)* needle
, size_t needlelen
) {
731 if (needlelen
> haystacklen
) return null;
732 auto h
= cast(const(ubyte)*)haystack
;
733 const(ubyte)* res
= null;
734 // size_t is unsigned
735 if (needlelen
> haystacklen || needlelen
== 0) return null;
738 while (pos
< haystacklen
-needlelen
+1) {
739 auto ff
= memmem(haystack
+pos
, haystacklen
-pos
, needle
, needlelen
);
740 if (ff
is null) break;
741 res
= cast(const(ubyte)*)ff
;
742 pos
= cast(size_t
)(res
-haystack
)+1;
744 return cast(void*)res
;
746 auto n
= cast(const(ubyte)*)needle
;
747 size_t len
= haystacklen
-needlelen
+1;
749 import core
.stdc
.string
: memcmp
;
750 auto ff
= cast(const(ubyte)*)memrchr(haystack
, *n
, len
);
751 if (ff
is null) break;
752 if (memcmp(ff
, needle
, needlelen
) == 0) return cast(void*)ff
;
753 //if (ff is h) break;
754 len
= cast(size_t
)(ff
-cast(ubyte*)haystack
);