1 /*
2  19.03.2018 12:58 - Применен алгоритм Максима Шибнева для fromUtf8to1251 (3-x кратное ускорение)
3  01.12.2017 17:57 - Темплате на toCON
4  13.08.2017  6:32 - Проверка и ускорение cp1251 -- Utf-8 -- cp1251
5  21.04.2016 18:13 - Проверка ИНН на корректность
6  31.05.2014 7:36:58
7  Add x64
8  Repair LTrim and RTrim
9  */
10 /*
11  ё - 184  0451  d1-91
12  Ё - 168  0401  d0-81
13  » -      00BB
14  « -      00AB
15  */
16 module asc1251;
17 
18 import std.ascii;
19 import std.conv;
20 import std.utf;
21 
22 
23 bool isDigit1251(char c)	pure nothrow { return (mm1251[c] & tDigit) != 0; }
24 
25 bool isLower1251E(char c)	pure nothrow { return (mm1251[c] & tEl) != 0;    }
26 
27 bool isUpper1251E(char c)	pure nothrow { return (mm1251[c] & tEu) != 0;    }
28 
29 bool isLower1251R(char c)	pure nothrow { return (mm1251[c] & tRl) != 0;    }
30 
31 bool isUpper1251R(char c)	pure nothrow { return (mm1251[c] & tRu) != 0;    }
32 
33 bool isLetters1251E(char c)	pure nothrow { return (mm1251[c] & (tEu + tEl)) != 0; }
34 
35 bool isLetters1251R(char c)	pure nothrow { return (mm1251[c] & (tRu + tRl)) != 0; }
36 
37 bool isLetters1251(char c)	pure nothrow { return (mm1251[c] & (tRu + tRl + tEu + tEl)) != 0; }
38 
39 bool isPrintLetters1251(char c) pure nothrow {	return (mm1251[c] & (tPrint)) != 0; }
40 
41 unittest {
42 	foreach (char c; "0123456789")
43 		assert(asc1251.isDigit1251(c));
44 	foreach (char c; lowercase)
45 		assert(asc1251.isLower1251E(c));
46 	foreach (char c; uppercase)
47 		assert(asc1251.isUpper1251E(c));
48 	foreach (char c; lowercase1251R)
49 		assert(asc1251.isLower1251R(c));
50 	foreach (char c; uppercase1251R)
51 		assert(asc1251.isUpper1251R(c));
52 	foreach (char c; uppercase ~ lowercase)
53 		assert(asc1251.isLetters1251E(c));
54 	foreach (char c; uppercase1251R ~ lowercase1251R)
55 		assert(asc1251.isLetters1251R(c));
56 }
57 
58 char[] LTrim1251(char[] str) {
59 	char[] rez;
60 	if (str.length == 0)
61 		return rez;
62 	for (auto i = 0; i < str.length; i++) {
63 		if (!isPrintLetters1251(str[i]))
64 			continue;
65 		rez = str[i .. $];
66 		break;
67 	}
68 	return rez;
69 }
70 
71 char[] RTrim1251(char[] str) {
72 	char[] rez;
73 	if (str.length == 0)
74 		return rez;
75 	for (auto i = str.length; i != 0; i--) {
76 		if (!isPrintLetters1251(str[i - 1]))
77 			continue;
78 		rez = str[0 .. i];
79 		break;
80 	}
81 	return rez;
82 }
83 
84 char[] Trim1251(char[] str) {
85 	return LTrim1251(RTrim1251(str));
86 }
87 
88 unittest {
89 	assert(LTrim1251(cast(char[]) "") == cast(char[]) "");
90 	assert(RTrim1251(cast(char[]) "") == cast(char[]) "");
91 	assert(LTrim1251(cast(char[]) "   Hello  ") == cast(char[]) "Hello  ");
92 	assert(RTrim1251(cast(char[]) "   Hello  ") == cast(char[]) "   Hello");
93 	assert(LTrim1251(cast(char[]) "   " ~ uppercase1251R) == cast(char[]) uppercase1251R);
94 	assert(LTrim1251(cast(char[]) "   " ~ lowercase1251R) == cast(char[]) lowercase1251R);
95 	assert(RTrim1251(lowercase1251R ~ cast(char[]) "   ") == cast(char[]) lowercase1251R);
96 	assert(Trim1251(cast(char[]) "   " ~ "1234567890" ~ "\x0E\x0F") == cast(char[]) "1234567890");
97 	assert(LTrim1251(cast(char[]) " " ~ cast(char[]) "1") == cast(char[]) "1");
98 }
99 
100 char toUpper1251(char c) {
101 	return isLower1251E(c) | isLower1251R(c) ? cast(char)(c - 32) : c;
102 }
103 
104 char[] toUpper1251(char[] str) {
105 	char[] rez;
106 	foreach (char c; str) {
107 		rez ~= toUpper1251(c);
108 	}
109 	return rez;
110 }
111 
112 char toLower1251(char c) {
113 	return isUpper1251E(c) | isUpper1251R(c) ? cast(char)(c + 32) : c;
114 }
115 
116 char[] toLower1251(char[] str) {
117 	char[] rez;
118 	foreach (char c; str) {
119 		rez ~= toLower1251(c);
120 	}
121 	return rez;
122 }
123 
124 char[] toFio1251(char[] str) {
125 	if (str.length == 0) {
126 		return str;
127 	} else {
128 		if (str.length == 1) {
129 			char[] rez;
130 			return rez ~= toUpper1251(str[0]);
131 		} else {
132 			return toUpper1251(str[0]) ~ toLower1251(str[1 .. $]);
133 		}
134 	}
135 }
136 
137 unittest {
138 	assert(toUpper1251('a') == 'A');
139 	foreach (char c; lowercase)
140 		assert(toUpper1251(c) == std.ascii.toUpper(c));
141 	foreach (char c; lowercase1251R)
142 		assert(toUpper1251(c) == uppercase1251R[c - 224]);
143 	assert(toUpper1251(cast(char[]) "hello[23]") == "HELLO[23]");
144 	assert(toUpper1251(cast(char[]) "") == "");
145 	assert(toLower1251(cast(char[]) "17(HELLO)") == "17(hello)");
146 	assert(toFio1251(cast(char[]) "HELLO!!!") == "Hello!!!");
147 	assert(toFio1251(cast(char[]) "") == "");
148 	assert(toFio1251(cast(char[]) "a") == "A");
149 }
150 
151 // Функция, возвращает подстроку используя разделитель.
152 char[] Split1251(char[] from, char rz, int poz) {
153 	char[] rez;
154 	int i, b, e, k;
155 	auto dLfrom = from.length;
156 	for (i = 0; i < dLfrom; i++) {
157 		if (from[i] == rz) {
158 			e = i;
159 			if (k == poz) {
160 				rez = from[b .. e]; // Есть начало и есть конец. Надо переписать
161 				return rez;
162 			} else {
163 				b = i + 1;
164 				k++;
165 			}
166 		}
167 	}
168 	if (poz == k)
169 		rez ~= from[b .. $];
170 	return rez;
171 }
172 
173 unittest {
174 	assert(Split1251(cast(char[]) "ABC|DEF", '|', 0) == "ABC");
175 	assert(Split1251(cast(char[]) "ABC|DEF", '|', 1) == "DEF");
176 	assert(Split1251(cast(char[]) "ABC|DEF", '|', 2) == "");
177 	assert(Split1251(cast(char[]) "ABC|DEF", '#', 2) == "");
178 	assert(Split1251(cast(char[]) "ABC|DEF", '#', 0) == "ABC|DEF");
179 }
180 // Шифрация-Дешифрация осуществляется в том же буфере в Win-1251 и AsciiZ
181 // sh  - T - шифрация, F - дешифрция
182 // str - указатель на строку
183 void shifr(bool sh, char* str) {
184 	char ch;
185 	int z;
186 
187 	if (sh) {
188 		z = -1;
189 	} else {
190 		z = +1;
191 	}
192 	for (char* i = str;; i++) {
193 		ch = *i;
194 		if (ch == 0)
195 			break;
196 		*i = cast(char)(ch + z);
197 	}
198 }
199 /* // Шифрует строки utf-8
200  // T - зашифровать, F - расшифровать
201  string shifr8(bool sh, string str) {
202  string rez; ubyte b;
203  if(str.length == 0) return rez;
204  if(sh) {
205  for(int i; i != str.length; i++) {
206  b = cast(ubyte)str[i];
207  if(b > 31) rez ~= "B" ~ (cast(char)(str[i]-1)); else rez ~= "A" ~ (cast(char)(str[i]+1));
208  }
209  }
210  else {
211  for(int i; i != str.length; i+=2) {
212  b = cast(ubyte)str[i];
213  if(b == 66) rez ~= (cast(char)(str[i+1]+1)); else rez ~= (cast(char)(str[i+1]-1));
214  }
215  }
216  return rez;
217  }
218  */
219  
220 
221 string shifr8n(T)(bool sh, T inStr) {
222 	string rez;
223 	ubyte b;
224 	string str = cast(string) inStr;
225 	return str;
226 	if (str.length == 0)
227 		return rez;
228 	if (sh) {
229 		for (int i; i != str.length; i++) {
230 			b = cast(ubyte) str[i];
231 			if (b > 31)
232 				rez ~= "B" ~ (cast(char)(str[i] - 1));
233 			else
234 				rez ~= "A" ~ (cast(char)(str[i] + 1));
235 		}
236 	} else {
237 		for (int i; i != str.length; i += 2) {
238 			b = cast(ubyte) str[i];
239 			if (b == 66)
240 				rez ~= (cast(char)(str[i + 1] + 1));
241 			else
242 				rez ~= (cast(char)(str[i + 1] - 1));
243 		}
244 	}
245 	return rez;
246 }
247 
248 // Проверка даты вида '27.12.2014' на корректность
249 // str = '27.12.2014'
250 // Return: T - коррктная дата
251 bool TestDate1251(char[] str) {
252 	bool rez = true;
253 	char[] s;
254 	char r = '.';
255 	if (str.length != 10)
256 		return false;
257 	s = Split1251(str, r, 0);
258 	if (s.length != 2)
259 		return false;
260 	else {
261 		if (!isDigit1251(s[0]) || !isDigit1251(s[1]))
262 			return false;
263 		int day = to!int(s);
264 		if (!(day > 0 && day < 32))
265 			return false;
266 	}
267 	s = Split1251(str, r, 1);
268 	if (s.length != 2)
269 		return false;
270 	else {
271 		if (!isDigit1251(s[0]) || !isDigit1251(s[1]))
272 			return false;
273 		int mes = to!int(s);
274 		if (!(mes > 0 && mes < 13))
275 			return false;
276 	}
277 	s = Split1251(str, r, 2);
278 	if (s.length != 4)
279 		return false;
280 	else {
281 		if (!isDigit1251(s[0]) || !isDigit1251(s[1]) || !isDigit1251(s[2]) || !isDigit1251(s[3]))
282 			return false;
283 		int yar = to!int(s);
284 		if (!(yar > 1900 && yar < 3000))
285 			return false;
286 	}
287 	return rez;
288 }
289 
290 // Проверка на соответствие ФИО, 'Иванов А.Н.', 1 большая, остальные маленькие и в конце инициалы
291 bool isFioii1251(char[] str) {
292 	bool rez = true;
293 	bool b1 = true;
294 	bool b2 = true;
295 	if (str.length < 6)
296 		return false;
297 	if (!(isUpper1251E(str[0]) || isUpper1251R(str[0])))
298 		return false;
299 	if (!((str[$ - 1] == '.') && (str[$ - 3] == '.')))
300 		return false;
301 	if (!(isUpper1251E(str[$ - 2]) || isUpper1251R(str[$ - 2])))
302 		return false;
303 	if (!(isUpper1251E(str[$ - 4]) || isUpper1251R(str[$ - 4])))
304 		return false;
305 	if (!(str[$ - 5] == ' '))
306 		return false;
307 	if (str.length > 6)
308 	foreach (char c; str[1 .. $ - 6]) {
309 		if (!(isLower1251E(c) || isLower1251R(c)))
310 			return false;
311 	}
312 	return rez;
313 }
314 
315 // Проверка на соответствие ФИО, 'Иванов', 1 большая, остальные маленькие
316 bool isFio1251(char[] str) {
317 	bool rez = true;
318 	bool b1 = true;
319 	bool b2 = true;
320 	if (str.length == 0)
321 		return false;
322 	if (!(isUpper1251E(str[0]) || isUpper1251R(str[0])))
323 		return false;
324 	foreach (char c; str[1 .. $]) {
325 		if (!(isLower1251E(c) || isLower1251R(c)))
326 			return false;
327 	}
328 	return rez;
329 }
330 // Проверка на соответствие 987, целое число
331 bool isInt1251(char[] str) {
332 	bool rez = true;
333 	bool b1 = true;
334 	bool b2 = true;
335 	if (str.length == 0)
336 		return false;
337 	foreach (char c; str[0 .. $]) {
338 		if (!isDigit(c))
339 			return false;
340 	}
341 	return rez;
342 }
343 
344 unittest {
345 	assert(TestDate1251(cast(char[]) "12.10.1961") == true);
346 	assert(TestDate1251(cast(char[]) "10.10.161") == false);
347 	assert(TestDate1251(cast(char[]) "00.10.1621") == false);
348 	assert(TestDate1251(cast(char[]) "31.10.1621") == false);
349 	assert(TestDate1251(cast(char[]) "32.10.2001") == false);
350 	assert(TestDate1251(cast(char[]) "31.12.1621") == false);
351 	assert(TestDate1251(cast(char[]) "31.13.2621") == false);
352 	assert(TestDate1251(cast(char[]) "31.13.3001") == false);
353 	// ------------------
354 	assert(isFio1251(cast(char[]) "Gena") == true);
355 	assert(isFio1251(cast(char[]) "Ge na") == false);
356 	assert(isFio1251(cast(char[]) "\xC3\xE5\xED\xE0") == true);
357 	assert(isFio1251(cast(char[]) "GenA") == false);
358 	assert(isFio1251(cast(char[]) "\xC3\xE5\xED\xC0") == false);
359 }
360 
361 // Проверка правильности ИНН string[10]
362 bool tstINN(string s) {
363 	string s1;
364 	bool rez;
365 	int[10] weights = [2, 4, 10, 3, 5, 9, 4, 6, 8, 0];
366 	int summ;
367 	
368 	if((s.length == 0) || (s.length > 10) ) return rez;
369 	foreach(ch; s) {
370 		if(!isDigit1251(ch)) return rez;
371 	}
372 	import std..string: format, strip;
373 	import std.conv: to;
374 	try {
375 		s1 = format("%.10s", to!long(strip(s)));
376 	} catch(Throwable) {
377 		return rez;			// Ошибка конвертации
378 	}
379 	if(s1 == "0000000000") return true;
380 	// Перебор цифр и вычисление суммы
381 	for(int i; i != 9; i++) {
382 		auto digit = s1[i] - 48; 
383 		summ += digit * weights[i];
384 	}
385 	auto ost = summ % 11;
386 	if (ost > 9) ost = ost % 10;
387 	if (ost == (s1[9] - 48)) rez = true;
388 	return rez;
389 }
390 
391 unittest {
392 	assert(tstINN("") == false);
393 	assert(tstINN("0000000000") == true);
394 	assert(tstINN("0") == true);
395 	assert(tstINN("0000A00000") == false);
396 	assert(tstINN("+000000000") == false);
397 	assert(tstINN("9999999999") == false);
398 	assert(tstINN("05911013765") == false);
399 
400 	assert(tstINN("5905033450") == true);
401 	assert(tstINN("5913001268") == true);
402 	assert(tstINN("6607000556") == true);
403 	assert(tstINN("5911013765") == true);
404 }
405 
406 char[] from1251toUtf8(char[] str) pure nothrow @trusted {
407 	char[] rez;
408 	foreach (char c1; str) rez ~= mm1251_Utf8[c1];
409 	return rez;
410 }
411 string from1251toUtf8(T)(T str) pure nothrow {
412 	char[] rez; 
413 	foreach (char c1; cast(char[])str) rez ~= mm1251_Utf8[c1];
414 	return cast(string)rez;
415 }
416 T1 fromUtf8to1251(T1, T2)(T2 str) {
417 	return to!(T1)(fromUtf8to1251(to!(char[])(str)));
418 }
419 
420 pragma(inline) size_t utf8Length(char[] src) pure nothrow @trusted {	size_t len; foreach (ref b; src) { if ((b & 0xC0) != 0x80) len++; } return len; }
421 char[] fromUtf8to1251(char[] str) pure
422 {
423 	if (str.length == 0) return str;
424 
425 	auto ret = new char[str.utf8Length];
426 	//auto ret = new char[str.length * 4];
427 	//char prb;
428 	size_t srcPos;
429 	size_t dstPos;
430 	size_t id;
431 
432 	while(srcPos < str.length) {
433 		id = stride(str, srcPos);
434 		switch (id) {
435 			case 1:
436 				ret[dstPos] = str[srcPos];
437 				break;
438 			case 2:
439 				switch (str[srcPos]) {
440 					case '\xD0':
441 						immutable prb = tbl_xD0[(str[srcPos + 1]) - 129];
442 						ret[dstPos] = ((prb == 0) ? '?' : prb);
443 						break;
444 					case '\xD1':
445 						immutable prb = tbl_xD1[(str[srcPos + 1]) - 128];
446 						ret[dstPos] = ((prb == 0) ? '2' : prb);
447 						break;
448 					case '\xD2':
449 						switch (str[srcPos + 1]) {
450 							case '\x91':
451 								ret[dstPos] = cast(char)180;
452 								break;
453 							case '\x90':
454 								ret[dstPos] = cast(char)165;
455 								break;
456 							default:
457 								ret[dstPos] = cast(char)7;
458 								break;
459 						}
460 						break;
461 					case '\xD3':
462 						break;
463 					case '\xC2':
464 						immutable prb = tbl_xC2[(str[srcPos + 1]) - 152];
465 						ret[dstPos] = ((prb == 0) ? '3' : prb);
466 						break;
467 					default:
468 						ret[dstPos] = '?';
469 						break;
470 				}
471 				break;
472 			case 3:
473 				if (str[srcPos] == '\xE2') {
474 					switch (str[srcPos + 1]) {
475 						case '\x80':
476 							immutable prb = tbl_x80[(str[srcPos + 2]) - 147];
477 							ret[dstPos] = ((prb == 0) ? '?' : prb);
478 							break;
479 						case '\x82':
480 							ret[dstPos] = ((str[srcPos + 2] == '\xAC') ? cast(char)136 : '?');
481 							break;
482 						case '\x84':
483 							switch (str[srcPos + 2]) {
484 								case '\x96':
485 									ret[dstPos] = (cast(char)185);
486 									break;
487 								case '\xA2':
488 									ret[dstPos] = (cast(char)153);
489 									break;
490 								default:
491 									ret[dstPos] = '?';
492 									break;
493 							}
494 							break;
495 						default:
496 							break;
497 					}
498 				}
499 				break;
500 			default: // 4, 5, 6
501 				break;
502 		} // switch (id)
503 
504 		srcPos += id;
505 		dstPos++;
506 	}
507 
508 	return ret;
509 }
510 
511 
512 unittest {
513 	assert(from1251toUtf8(cast(char[]) "\xC3\xE5\xED\xE0") == "Гена");
514 	assert(from1251toUtf8(cast(char[]) "Gena123") == "Gena123");
515 
516 	assert(fromUtf8to1251(cast(char[]) "Гена") == "\xC3\xE5\xED\xE0");
517 	assert(fromUtf8to1251(cast(char[]) "Gena123") == "Gena123");
518 	char[] g = [ 'G', 'e', 'n', 'a', '1', '2', '3' ];
519 	assert(fromUtf8to1251!(char[])("Gena123") == g);
520 	assert(fromUtf8to1251!(char[])("Гена") == "\xC3\xE5\xED\xE0");
521 
522 }
523 
524 char[] from1251to866(char[] str) {
525 	if (str.length == 0) return str;
526 	int dlStr = str.length;
527 	auto ret = new char[dlStr];	for(int i; i != dlStr; i++) ret[i] = _1251_866[str[i]];
528 	return ret;
529 }
530 
531 string toCON(T)(T s) {
532 	version (Windows) {
533 		return to!string(from1251to866(fromUtf8to1251(cast(char[]) s)));
534 	}
535 	version (linux) {
536 		return cast(string)s;
537 	}
538 	version (OSX) {
539 		return cast(string)s;
540 	}
541 }
542 string char1251toUtf8(char ch) {
543 	return mm1251_Utf8[ch];
544 }
545 
546 private:
547 
548 const int sByte = ubyte.max + 1;
549 
550 const tBad = 0; // Бяка
551 const tDigit = 1; // Цифра
552 const tEl = 2; // Анг Маленькие
553 const tEu = 4; // Анг Большие
554 const tPrint = 8; // Печатные
555 const tRl = 16; // Рус Маленькие
556 const tRu = 32; // Рус Большие
557 
558 private immutable char[][sByte]  mm1251_Utf8= [
559 	/* 0 */
560 	"\x00", /* 1 */ "\x01", /* 2 */ "\x02", /* 3 */ "\x03", /* 4 */ "\x04",/* 5 */
561 	"\x05", /* 6 */ "\x06", /* 7 */ "\x07", /* 8 */ "\x08", /* 9 */ "\x09",/* 10 */
562 	"\x0A", /* 11 */ "\x0B", /* 12 */ "\x0C", /* 13 */ "\x0D", /* 14 */ "\x0E",/* 15 */
563 	"\x0F", /* 16 */ "\x10", /* 17 */ "\x11", /* 18 */ "\x12", /* 19 */ "\x13",/* 20 */
564 	"\x14", /* 21 */ "\x15", /* 22 */ "\x16", /* 23 */ "\x17", /* 24 */ "\x18",/* 25 */
565 	"\x19", /* 26 */ "\x1A", /* 27 */ "\x1B", /* 28 */ "\x1C", /* 29 */ "\x1D",/* 30 */
566 	"\x1E", /* 31 */ "\x1F", /* 32 */ "\x20", /* 33 */ "\x21", /* 34 */ "\x22",/* 35 */
567 	"\x23", /* 36 */ "\x24", /* 37 */ "\x25", /* 38 */ "\x26", /* 39 */ "\x27",/* 40 */
568 	"\x28", /* 41 */ "\x29", /* 42 */ "\x2A", /* 43 */ "\x2B", /* 44 */ "\x2C",/* 45 */
569 	"\x2D", /* 46 */ "\x2E", /* 47 */ "\x2F", /* 48 */ "\x30", /* 49 */ "\x31",/* 50 */
570 	"\x32", /* 51 */ "\x33", /* 52 */ "\x34", /* 53 */ "\x35", /* 54 */ "\x36",/* 55 */
571 	"\x37", /* 56 */ "\x38", /* 57 */ "\x39", /* 58 */ "\x3A", /* 59 */ "\x3B",/* 60 */
572 	"\x3C", /* 61 */ "\x3D", /* 62 */ "\x3E", /* 63 */ "\x3F", /* 64 */ "\x40",/* 65 */
573 	"\x41", /* 66 */ "\x42", /* 67 */ "\x43", /* 68 */ "\x44", /* 69 */ "\x45",/* 70 */
574 	"\x46", /* 71 */ "\x47", /* 72 */ "\x48", /* 73 */ "\x49", /* 74 */ "\x4A",/* 75 */
575 	"\x4B", /* 76 */ "\x4C", /* 77 */ "\x4D", /* 78 */ "\x4E", /* 79 */ "\x4F",/* 80 */
576 	"\x50", /* 81 */ "\x51", /* 82 */ "\x52", /* 83 */ "\x53", /* 84 */ "\x54",/* 85 */
577 	"\x55", /* 86 */ "\x56", /* 87 */ "\x57", /* 88 */ "\x58", /* 89 */ "\x59",/* 90 */
578 	"\x5A", /* 91 */ "\x5B", /* 92 */ "\x5C", /* 93 */ "\x5D", /* 94 */ "\x5E",/* 95 */
579 	"\x5F", /* 96 */ "\x60", /* 97 */ "\x61", /* 98 */ "\x62", /* 99 */ "\x63",/* 100 */
580 	"\x64", /* 101 */ "\x65", /* 102 */ "\x66", /* 103 */ "\x67", /* 104 */ "\x68",/* 105 */
581 	"\x69", /* 106 */ "\x6A", /* 107 */ "\x6B", /* 108 */ "\x6C", /* 109 */ "\x6D",/* 110 */
582 	"\x6E", /* 111 */ "\x6F", /* 112 */ "\x70", /* 113 */ "\x71", /* 114 */ "\x72",/* 115 */
583 	"\x73", /* 116 */ "\x74", /* 117 */ "\x75", /* 118 */ "\x76", /* 119 */ "\x77",/* 120 */
584 	"\x78", /* 121 */ "\x79", /* 122 */ "\x7A", /* 123 */ "\x7B", /* 124 */ "\x7C",/* 125 */
585 	"\x7D", /* 126 */ "\x7E", /* 127 */ "\x7F", /* 128 */ "\xD0\x82", /* 129 */ "\xD0\x83",
586 	/* 130 */
587 	"\xE2\x80\x9A", /* 131 */ "\xD1\x93", /* 132 */ "\xE2\x80\x9E", /* 133 */ "\xE2\x80\xA6", /* 134 */ "\xE2\x80\xA0", /* 135 */ "\xE2\x80\xA1",
588 	/* 136 */
589 	"\xE2\x82\xAC", /* 137 */ "\xE2\x80\xB0", /* 138 */ "\xD0\x89", /* 139 */ "\xE2\x80\xB9", /* 140 */ "\xD0\x8A", /* 141 */ "\xD0\x8C",
590 	/* 142 */
591 	"\xD0\x8B", /* 143 */ "\xD0\x8F", /* 144 */ "\xD1\x92", /* 145 */ "\xE2\x80\x98", /* 146 */ "\xE2\x80\x99", /* 147 */ "\xE2\x80\x9C",
592 	/* 148 */
593 	"\xE2\x80\x9D", /* 149 */ "\xE2\x80\xA2", /* 150 */ "\xE2\x80\x93", /* 151 */ "\xE2\x80\x94", /* 152 */ "\xC2\x98", /* 153 */ "\xE2\x84\xA2",
594 	/* 154 */
595 	"\xD1\x99", /* 155 */ "\xE2\x80\xBA", /* 156 */ "\xD1\x9A", /* 157 */ "\xD1\x9C", /* 158 */ "\xD1\x9B", /* 159 */ "\xD1\x9F",
596 	/* 160 */
597 	"\xC2\xA0", /* 161 */ "\xD0\x8E", /* 162 */ "\xD1\x9E", /* 163 */ "\xD0\x88", /* 164 */ "\xC2\xA4", /* 165 */ "\xD2\x90",
598 	/* 166 */
599 	"\xC2\xA6", /* 167 */ "\xC2\xA7", /* 168 */ "\xD0\x81", /* 169 */ "\xC2\xA9", /* 170 */ "\xD0\x84", /* 171 */ "\xC2\xAB",
600 	/* 172 */
601 	"\xC2\xAC", /* 173 */ "\xC2\xAD", /* 174 */ "\xC2\xAE", /* 175 */ "\xD0\x87", /* 176 */ "\xC2\xB0", /* 177 */ "\xC2\xB1",
602 	/* 178 */
603 	"\xD0\x86", /* 179 */ "\xD1\x96", /* 180 */ "\xD2\x91", /* 181 */ "\xC2\xB5", /* 182 */ "\xC2\xB6", /* 183 */ "\xC2\xB7",
604 	/* 184 */
605 	"\xD1\x91", /* 185 */ "\xE2\x84\x96", /* 186 */ "\xD1\x94", /* 187 */ "\xC2\xBB", /* 188 */ "\xD1\x98", /* 189 */ "\xD0\x85",
606 	/* 190 */
607 	"\xD1\x95", /* 191 */ "\xD1\x97", /* 192 */ "\xD0\x90", /* 193 */ "\xD0\x91",/* 194 */
608 	"\xD0\x92", /* 195 */ "\xD0\x93", /* 196 */ "\xD0\x94", /* 197 */ "\xD0\x95",
609 	/* 198 */
610 	"\xD0\x96", /* 199 */ "\xD0\x97", /* 200 */ "\xD0\x98", /* 201 */ "\xD0\x99",/* 202 */
611 	"\xD0\x9A", /* 203 */ "\xD0\x9B", /* 204 */ "\xD0\x9C", /* 205 */ "\xD0\x9D",
612 	/* 206 */
613 	"\xD0\x9E", /* 207 */ "\xD0\x9F", /* 208 */ "\xD0\xA0", /* 209 */ "\xD0\xA1",/* 210 */
614 	"\xD0\xA2", /* 211 */ "\xD0\xA3", /* 212 */ "\xD0\xA4", /* 213 */ "\xD0\xA5",
615 	/* 214 */
616 	"\xD0\xA6", /* 215 */ "\xD0\xA7", /* 216 */ "\xD0\xA8", /* 217 */ "\xD0\xA9",/* 218 */
617 	"\xD0\xAA", /* 219 */ "\xD0\xAB", /* 220 */ "\xD0\xAC", /* 221 */ "\xD0\xAD",
618 	/* 222 */
619 	"\xD0\xAE", /* 223 */ "\xD0\xAF", /* 224 */ "\xD0\xB0", /* 225 */ "\xD0\xB1",/* 226 */
620 	"\xD0\xB2", /* 227 */ "\xD0\xB3", /* 228 */ "\xD0\xB4", /* 229 */ "\xD0\xB5",
621 	/* 230 */
622 	"\xD0\xB6", /* 231 */ "\xD0\xB7", /* 232 */ "\xD0\xB8", /* 233 */ "\xD0\xB9",/* 234 */
623 	"\xD0\xBA", /* 235 */ "\xD0\xBB", /* 236 */ "\xD0\xBC", /* 237 */ "\xD0\xBD",
624 	/* 238 */
625 	"\xD0\xBE", /* 239 */ "\xD0\xBF", /* 240 */ "\xD1\x80", /* 241 */ "\xD1\x81",/* 242 */
626 	"\xD1\x82", /* 243 */ "\xD1\x83", /* 244 */ "\xD1\x84", /* 245 */ "\xD1\x85",
627 	/* 246 */
628 	"\xD1\x86", /* 247 */ "\xD1\x87", /* 248 */ "\xD1\x88", /* 249 */ "\xD1\x89",/* 250 */
629 	"\xD1\x8A", /* 251 */ "\xD1\x8B", /* 252 */ "\xD1\x8C", /* 253 */ "\xD1\x8D",
630 	/* 254 */
631 	"\xD1\x8E", /* 255 */ "\xD1\x8F"
632 ];
633 
634 private immutable int[sByte]  mm1251= [/* 0 */
635 	tBad, /* 1 */ tBad, /* 2 */ tBad, /* 3 */ tBad, /* 4 */ tBad, /* 5 */ tBad, /* 6 */ tBad, /* 7 */ tBad, /* 8 */ tBad,
636 	/* 9 */
637 	tBad, /* 10 */ tBad, /* 11 */ tBad, /* 12 */ tBad, /* 13 */ tBad, /* 14 */ tBad, /* 15 */ tBad, /* 16 */ tBad, /* 17 */ tBad,
638 	/* 18 */
639 	tBad, /* 19 */ tBad, /* 20 */ tBad, /* 21 */ tBad, /* 22 */ tBad, /* 23 */ tBad, /* 24 */ tBad, /* 25 */ tBad, /* 26 */ tBad,
640 	/* 27 */
641 	tBad, /* 28 */ tBad, /* 29 */ tBad, /* 30 */ tBad, /* 31 */ tBad, /* 32 */ tBad, /* 33 */ tPrint, /* 34 */ tPrint, /* 35 */ tPrint,
642 	/* 36 */
643 	tPrint, /* 37 */ tPrint, /* 38 */ tPrint, /* 39 */ tPrint, /* 40 */ tPrint, /* 41 */ tPrint, /* 42 */ tPrint, /* 43 */ tPrint, /* 44 */ tPrint,
644 	/* 45 */
645 	tPrint, /* 46 */ tPrint, /* 47 */ tPrint, /* 48 */ tPrint + tDigit, /* 49 */ tPrint + tDigit, /* 50 */ tPrint + tDigit, /* 51 */ tPrint + tDigit,
646 	/* 52 */
647 	tPrint + tDigit, /* 53 */ tPrint + tDigit, /* 54 */ tPrint + tDigit, /* 55 */ tPrint + tDigit,
648 	/* 56 */
649 	tPrint + tDigit, /* 57 */ tPrint + tDigit, /* 58 */ tPrint, /* 59 */ tPrint, /* 60 */ tPrint, /* 61 */ tPrint,
650 	/* 62 */
651 	tPrint, /* 63 */ tPrint, /* 64 */ tPrint,/* 65 */
652 	tPrint + tEu, /* 66 */ tPrint + tEu, /* 67 */ tPrint + tEu, /* 68 */ tPrint + tEu, /* 69 */ tPrint + tEu, /* 70 */ tPrint + tEu,
653 	/* 71 */
654 	tPrint + tEu, /* 72 */ tPrint + tEu, /* 73 */ tPrint + tEu, /* 74 */ tPrint + tEu, /* 75 */ tPrint + tEu, /* 76 */ tPrint + tEu,
655 	/* 77 */
656 	tPrint + tEu, /* 78 */ tPrint + tEu, /* 79 */ tPrint + tEu, /* 80 */ tPrint + tEu, /* 81 */ tPrint + tEu, /* 82 */ tPrint + tEu,
657 	/* 83 */
658 	tPrint + tEu, /* 84 */ tPrint + tEu, /* 85 */ tPrint + tEu, /* 86 */ tPrint + tEu, /* 87 */ tPrint + tEu, /* 88 */ tPrint + tEu,
659 	/* 89 */
660 	tPrint + tEu, /* 90 */ tPrint + tEu,/* 91 */
661 	tPrint, /* 92 */ tPrint, /* 93 */ tPrint, /* 94 */ tPrint, /* 95 */ tPrint,
662 	/* 96 */
663 	tPrint,/* 97 */
664 	tPrint + tEl, /* 98 */ tPrint + tEl, /* 99 */ tPrint + tEl, /* 100 */ tPrint + tEl, /* 101 */ tPrint + tEl, /* 102 */ tPrint + tEl,
665 	/* 103 */
666 	tPrint + tEl, /* 104 */ tPrint + tEl, /* 105 */ tPrint + tEl, /* 106 */ tPrint + tEl, /* 107 */ tPrint + tEl, /* 108 */ tPrint + tEl,
667 	/* 109 */
668 	tPrint + tEl, /* 110 */ tPrint + tEl, /* 111 */ tPrint + tEl, /* 112 */ tPrint + tEl, /* 113 */ tPrint + tEl, /* 114 */ tPrint + tEl,
669 	/* 115 */
670 	tPrint + tEl, /* 116 */ tPrint + tEl, /* 117 */ tPrint + tEl, /* 118 */ tPrint + tEl, /* 119 */ tPrint + tEl, /* 120 */ tPrint + tEl,
671 	/* 121 */
672 	tPrint + tEl, /* 122 */ tPrint + tEl, /* 123 */ tPrint, /* 124 */ tPrint, /* 125 */ tPrint, /* 126 */ tPrint, /* 127 */ tPrint, /* 128 */ tPrint,
673 	/* 129 */
674 	tPrint,/* 130 */
675 	tPrint, /* 131 */ tPrint, /* 132 */ tPrint, /* 133 */ tPrint, /* 134 */ tPrint, /* 135 */ tPrint, /* 136 */ tPrint, /* 137 */ tPrint,/* 138 */
676 	tPrint, /* 139 */ tPrint, /* 140 */ tPrint, /* 141 */ tPrint, /* 142 */ tPrint, /* 143 */ tPrint, /* 144 */ tPrint, /* 145 */ tPrint,/* 146 */
677 	tPrint, /* 147 */ tPrint, /* 148 */ tPrint, /* 149 */ tPrint, /* 150 */ tPrint, /* 151 */ tPrint, /* 152 */ tPrint, /* 153 */ tPrint,/* 154 */
678 	tPrint, /* 155 */ tPrint, /* 156 */ tPrint, /* 157 */ tPrint, /* 158 */ tPrint, /* 159 */ tPrint, /* 160 */ tPrint, /* 161 */ tPrint,/* 162 */
679 	tPrint, /* 163 */ tPrint, /* 164 */ tPrint, /* 165 */ tPrint, /* 166 */ tPrint, /* 167 */ tPrint, /* 168 */ tPrint + tRu, /* 169 */ tPrint,
680 	/* 170 */
681 	tPrint, /* 171 */ tPrint, /* 172 */ tPrint, /* 173 */ tPrint, /* 174 */ tPrint, /* 175 */ tPrint, /* 176 */ tPrint, /* 177 */ tPrint,/* 178 */
682 	tPrint, /* 179 */ tPrint, /* 180 */ tPrint, /* 181 */ tPrint, /* 182 */ tPrint, /* 183 */ tPrint, /* 184 */ tPrint + tRl, /* 185 */ tPrint,
683 	/* 186 */
684 	tPrint, /* 187 */ tPrint, /* 188 */ tPrint, /* 189 */ tPrint, /* 190 */ tPrint, /* 191 */ tPrint, /* 192 */ tPrint + tRu,
685 	/* 193 */
686 	tPrint + tRu, /* 194 */ tPrint + tRu, /* 195 */ tPrint + tRu, /* 196 */ tPrint + tRu, /* 197 */ tPrint + tRu, /* 198 */ tPrint + tRu,
687 	/* 199 */
688 	tPrint + tRu, /* 200 */ tPrint + tRu, /* 201 */ tPrint + tRu, /* 202 */ tPrint + tRu, /* 203 */ tPrint + tRu, /* 204 */ tPrint + tRu,
689 	/* 205 */
690 	tPrint + tRu, /* 206 */ tPrint + tRu, /* 207 */ tPrint + tRu, /* 208 */ tPrint + tRu, /* 209 */ tPrint + tRu, /* 210 */ tPrint + tRu,
691 	/* 211 */
692 	tPrint + tRu, /* 212 */ tPrint + tRu, /* 213 */ tPrint + tRu, /* 214 */ tPrint + tRu, /* 215 */ tPrint + tRu, /* 216 */ tPrint + tRu,
693 	/* 217 */
694 	tPrint + tRu, /* 218 */ tPrint + tRu, /* 219 */ tPrint + tRu, /* 220 */ tPrint + tRu, /* 221 */ tPrint + tRu, /* 222 */ tPrint + tRu,
695 	/* 223 */
696 	tPrint + tRu, /* 224 */ tPrint + tRl, /* 225 */ tPrint + tRl, /* 226 */ tPrint + tRl, /* 227 */ tPrint + tRl, /* 228 */ tPrint + tRl,
697 	/* 229 */
698 	tPrint + tRl, /* 230 */ tPrint + tRl, /* 231 */ tPrint + tRl, /* 232 */ tPrint + tRl, /* 233 */ tPrint + tRl, /* 234 */ tPrint + tRl,
699 	/* 235 */
700 	tPrint + tRl, /* 236 */ tPrint + tRl, /* 237 */ tPrint + tRl, /* 238 */ tPrint + tRl, /* 239 */ tPrint + tRl, /* 240 */ tPrint + tRl,
701 	/* 241 */
702 	tPrint + tRl, /* 242 */ tPrint + tRl, /* 243 */ tPrint + tRl, /* 244 */ tPrint + tRl, /* 245 */ tPrint + tRl, /* 246 */ tPrint + tRl,
703 	/* 247 */
704 	tPrint + tRl, /* 248 */ tPrint + tRl, /* 249 */ tPrint + tRl, /* 250 */ tPrint + tRl, /* 251 */ tPrint + tRl, /* 252 */ tPrint + tRl,
705 	/* 253 */
706 	tPrint + tRl, /* 254 */ tPrint + tRl, /* 255 */ tPrint + tRl];
707 
708 // char mm1251u[sByte];
709 private immutable uppercase1251R = "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF"; /// А..Я
710 private immutable lowercase1251R = "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; /// А..Я
711 private immutable _1251_866 = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x18\x19\x1A\x1B......\x20!\x22#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~.+++++++++++++++++++++++++++++++++++++++1\xF0345+++++++++++1\xF1\xFC++++++\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF";
712 private immutable char[62] tbl_xD1 = [
713 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,  0,184,144,131,186,190,
714 179,191,188,154,156,158,157,  0,162,159,  0,  0,210,211,212,213,214,215,216,217,218,219,
715 220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237
716 ];
717 private immutable char[63] tbl_xD0 = [
718 168,128,129,170,189,178,175,163,138,140,142,141,  0,161,143,192,193,194,195,196,197,198,
719 199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,
720 221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239
721 ];
722 private immutable char[40] tbl_x80 = [
723 150,151,  0,  0,  0,145,146,130,  0,147,148,132,  0,134,135,149,  0,  0,  0,133,  0,  0,
724   0,  0,  0,  0,  0,  0,  0,137,  0,  0,  0,  0,  0,  0,  0,  0,139,155
725 ];
726 private immutable char[36] tbl_xC2 = [
727 152,  0,  0,  0,  0,  0,  0,  0,160,  0,  0,  0,164,  0,166,167,  0,169,  0,171,172,173,
728 174,  0,176,177,  0,  0,  0,181,182,183,  0,  0,  0,187
729 ];
730 
731 bool isAtr1251(char c, int atr) {
732 	return (mm1251[c] & atr) != 0;
733 }