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