1 module bc..string.ascii;
2 
3 /**
4  * Simplified ASCII strings case insensitve comparison.
5  * Returns:
6  *   - < 0 - when first string is lesser that the second
7  *   - = 0 - when both string are equal
8  *   - > 0 - when first string is greater than the second
9  */
10 int sicmp(const(char)[] a, const(char)[] b) @safe nothrow @nogc
11 {
12     immutable len = a.length > b.length ? b.length : a.length;
13     version (Posix) {
14         import core.sys.posix.strings : strncasecmp;
15         immutable diff = () @trusted { return strncasecmp(a.ptr, b.ptr, len); }();
16         if (diff) return diff;
17     } else {
18         // TODO: manual loop unroll
19         immutable end = min(a.length, b.length);
20         for (int i=0; i < len; ++i) {
21             auto lhs = a[i].toLower;
22             auto rhs = b[i].toLower;
23             auto diff = lhs - rhs;
24             if (diff) return diff;
25         }
26     }
27     return (a.length > b.length) - (b.length > a.length);
28 }
29 
30 @safe unittest
31 {
32     assert(sicmp("fOoo",  "FoOo")  == 0);
33     assert(sicmp("abcd",  "efgh")  <  0);
34     assert(sicmp("efgh",  "abcd")  >  0);
35     assert(sicmp("fOoox", "FoOo")  >  0);
36     assert(sicmp("fOoo",  "FoOox") <  0);
37 }
38 
39 /// Converts ASCII characters 'A'..'Z' to a lower 'a'..'z'.
40 char toLower(char c) @safe pure nothrow @nogc
41 {
42     pragma(inline, true);
43     static immutable chmap = ()
44         {
45             char[256] res = void;
46 
47             for (int i=0; i < 256; ++i) {
48                 if (i >= 'A' && i <= 'Z') res[i] = cast(char)(i+32);
49                 else res[i] = cast(char)i;
50             }
51             return res;
52         }();
53 
54     return chmap[c];
55 }
56 
57 /// Converts ASCII characters 'a'..'z' to a upper 'A'..'Z'.
58 char toUpper(char c) @safe pure nothrow @nogc
59 {
60     pragma(inline, true);
61     static immutable chmap = ()
62         {
63             char[256] res = void;
64 
65             for (int i=0; i < 256; ++i) {
66                 if (i >= 'a' && i <= 'z') res[i] = cast(char)(i-32);
67                 else res[i] = cast(char)i;
68             }
69             return res;
70         }();
71 
72     return chmap[c];
73 }
74 
75 //@safe
76 unittest
77 {
78     assert('A'.toLower == 'a');
79     assert('b'.toLower == 'b');
80     assert('2'.toLower == '2');
81     assert('Z'.toLower == 'z');
82 
83     assert('a'.toUpper == 'A');
84     assert('B'.toUpper == 'B');
85     assert('2'.toUpper == '2');
86     assert('z'.toUpper == 'Z');
87 }
88 
89 /// Checks if character is a digit ('0'..'9')
90 bool isDigit(char c) @safe pure nothrow @nogc
91 {
92     pragma(inline, true);
93     static immutable chmap = ()
94         {
95             bool[256] res = void;
96 
97             for (int i=0; i < 256; ++i) {
98                 if (i >= '0' && i <= '9') res[i] = true;
99                 else res[i] = false;
100             }
101             return res;
102         }();
103 
104     return chmap[c];
105 }
106 
107 @safe unittest
108 {
109     assert('0'.isDigit);
110     assert('9'.isDigit);
111     assert(!'a'.isDigit);
112     assert(!'+'.isDigit);
113 }