1 /*******************************************************************************
2 
3     Placeholder for a selection of ASCII utilities. These generally will
4     not work with utf8, and cannot be easily extended to utf16 or utf32
5 
6     Copyright:
7         Copyright (c) 2006 Kris Bell.
8         Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
9         All rights reserved.
10 
11     License:
12         Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
13         See LICENSE_TANGO.txt for details.
14 
15     Version: Dec 2006: Initial release
16 
17     Authors: Kris
18 
19 *******************************************************************************/
20 
21 module ocean.text.Ascii;
22 
23 import ocean.core.Array;
24 import ocean.core.Verify;
25 import ocean.meta.types.Qualifiers;
26 
27 import core.stdc.string: memcmp;
28 import core.sys.posix.strings : strncasecmp;
29 
30 version (unittest) import ocean.core.Test;
31 
32 /******************************************************************************
33 
34     Convert to lowercase. Performs in-place conversion.
35 
36     Params:
37         src = text to convert
38 
39     Returns:
40         slice of src after conversion
41 
42 *******************************************************************************/
43 
44 
45 public mstring toLower ( mstring src )
46 {
47     foreach (ref c; src)
48         if (c>= 'A' && c <= 'Z')
49             c = cast(char)(c + 32);
50     return src;
51 }
52 
53 /******************************************************************************
54 
55     Convert to lowercase. Result is written to resized buffer.
56 
57     Params:
58         src = text to convert
59         dst = buffer to write result to
60 
61     Returns:
62         slice of dst after conversion
63 
64 *******************************************************************************/
65 
66 public mstring toLower ( cstring src, ref mstring dst )
67 {
68     dst.copy(src);
69     return toLower(dst);
70 }
71 
72 /******************************************************************************
73 
74     Convert to uppercase. Performs in-place conversion.
75 
76     Params:
77         src = text to convert
78 
79     Returns:
80         slice of src after conversion
81 
82 *******************************************************************************/
83 
84 public mstring toUpper ( mstring src )
85 {
86     foreach (ref c; src)
87         if (c>= 'a' && c <= 'z')
88             c = cast(char)(c - 32);
89     return src;
90 }
91 
92 /******************************************************************************
93 
94     Convert to uppercase. Result is written to resized buffer.
95 
96     Params:
97         src = text to convert
98         dst = buffer to write result to
99 
100     Returns:
101         slice of dst after conversion
102 
103 *******************************************************************************/
104 
105 public mstring toUpper ( cstring src, ref mstring dst )
106 {
107     dst.copy(src);
108     return toUpper(dst);
109 }
110 
111 /******************************************************************************
112 
113   Compare two char[] ignoring case. Returns 0 if equal
114 
115  ******************************************************************************/
116 
117 int icompare (cstring s1, cstring s2)
118 {
119     auto len = s1.length;
120     if (s2.length < len)
121         len = s2.length;
122 
123     auto result = strncasecmp(s1.ptr, s2.ptr, len);
124 
125     if (result is 0)
126         result = cast(int) (s1.length - s2.length);
127     return result;
128 }
129 
130 
131 /******************************************************************************
132 
133   Compare two char[] with case. Returns 0 if equal
134 
135  ******************************************************************************/
136 
137 int compare (cstring s1, cstring s2)
138 {
139     auto len = s1.length;
140     if (s2.length < len)
141         len = s2.length;
142 
143     auto result = memcmp (s1.ptr, s2.ptr, len);
144 
145     if (result is 0)
146         result = cast(int) (s1.length - s2.length);
147     return result;
148 }
149 
150 
151 
152 /******************************************************************************
153 
154   Return the index position of a text pattern within src, or
155   src.length upon failure.
156 
157   This is a case-insensitive search (with thanks to Nietsnie)
158 
159  ******************************************************************************/
160 
161 static int isearch (in cstring src, in cstring pattern)
162 {
163     static  char[] _caseMap = [
164         '\000','\001','\002','\003','\004','\005','\006','\007',
165         '\010','\011','\012','\013','\014','\015','\016','\017',
166         '\020','\021','\022','\023','\024','\025','\026','\027',
167         '\030','\031','\032','\033','\034','\035','\036','\037',
168         '\040','\041','\042','\043','\044','\045','\046','\047',
169         '\050','\051','\052','\053','\054','\055','\056','\057',
170         '\060','\061','\062','\063','\064','\065','\066','\067',
171         '\070','\071','\072','\073','\074','\075','\076','\077',
172         '\100','\141','\142','\143','\144','\145','\146','\147',
173         '\150','\151','\152','\153','\154','\155','\156','\157',
174         '\160','\161','\162','\163','\164','\165','\166','\167',
175         '\170','\171','\172','\133','\134','\135','\136','\137',
176         '\140','\141','\142','\143','\144','\145','\146','\147',
177         '\150','\151','\152','\153','\154','\155','\156','\157',
178         '\160','\161','\162','\163','\164','\165','\166','\167',
179         '\170','\171','\172','\173','\174','\175','\176','\177',
180         '\200','\201','\202','\203','\204','\205','\206','\207',
181         '\210','\211','\212','\213','\214','\215','\216','\217',
182         '\220','\221','\222','\223','\224','\225','\226','\227',
183         '\230','\231','\232','\233','\234','\235','\236','\237',
184         '\240','\241','\242','\243','\244','\245','\246','\247',
185         '\250','\251','\252','\253','\254','\255','\256','\257',
186         '\260','\261','\262','\263','\264','\265','\266','\267',
187         '\270','\271','\272','\273','\274','\275','\276','\277',
188         '\300','\341','\342','\343','\344','\345','\346','\347',
189         '\350','\351','\352','\353','\354','\355','\356','\357',
190         '\360','\361','\362','\363','\364','\365','\366','\367',
191         '\370','\371','\372','\333','\334','\335','\336','\337',
192         '\340','\341','\342','\343','\344','\345','\346','\347',
193         '\350','\351','\352','\353','\354','\355','\356','\357',
194         '\360','\361','\362','\363','\364','\365','\366','\367',
195         '\370','\371','\372','\373','\374','\375','\376','\377',
196     ];
197 
198 
199     verify(src.ptr !is null);
200     verify(pattern.ptr !is null);
201 
202     for (int i1=0, i2; i1 <= cast(int)(src.length - pattern.length); ++i1)
203     {
204         for (i2=0; i2 < pattern.length; ++i2)
205             if (_caseMap[src[i1 + i2]] != _caseMap[pattern[i2]])
206                 break;
207 
208         if (i2 is pattern.length)
209             return i1;
210     }
211     return cast(int) src.length;
212 }
213 
214 
215 
216 /******************************************************************************
217 
218  ******************************************************************************/
219 
220 unittest
221 {
222     char[] tmp;
223 
224     test (toLower("1bac", tmp) == "1bac");
225     test (toLower("1BAC", tmp) == "1bac");
226     test (toUpper("1bac", tmp) == "1BAC");
227     test (toUpper("1BAC", tmp) == "1BAC");
228     test (icompare ("ABC", "abc") is 0);
229     test (icompare ("abc", "abc") is 0);
230     test (icompare ("abcd", "abc") > 0);
231     test (icompare ("abc", "abcd") < 0);
232     test (icompare ("ACC", "abc") > 0);
233 
234     test (isearch ("ACC", "abc") is 3);
235     test (isearch ("ACC", "acc") is 0);
236     test (isearch ("aACC", "acc") is 1);
237 }
238 
239 debug (Ascii)
240 {
241     void main() {}
242 }