Merge pull request #799 from kebby/master
[mono.git] / mcs / class / I18N / CJK / ISO2022JP.cs
1 //
2 // ISO2022JP.cs
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 using System;
8 using System.Text;
9 using I18N.Common;
10
11 #if DISABLE_UNSAFE
12 using MonoEncoder = I18N.Common.MonoSafeEncoder;
13 using MonoEncoding = I18N.Common.MonoSafeEncoding;
14 #endif
15
16 namespace I18N.CJK
17 {
18         [Serializable]
19         public class CP50220 : ISO2022JPEncoding
20         {
21                 public CP50220 ()
22                         : base (50220, false, false)
23                 {
24                 }
25
26                 public override string EncodingName {
27                         get { return "Japanese (JIS)"; }
28                 }
29         }
30
31         [Serializable]
32         public class CP50221 : ISO2022JPEncoding
33         {
34                 public CP50221 ()
35                         : base (50221, true, false)
36                 {
37                 }
38
39                 public override string EncodingName {
40                         get { return "Japanese (JIS-Allow 1 byte Kana)"; }
41                 }
42         }
43
44         [Serializable]
45         public class CP50222 : ISO2022JPEncoding
46         {
47                 public CP50222 ()
48                         : base (50222, true, true)
49                 {
50                 }
51
52                 public override string EncodingName {
53                         get { return "Japanese (JIS-Allow 1 byte Kana - SO/SI)"; }
54                 }
55         }
56
57         [Serializable]
58         public class ISO2022JPEncoding : MonoEncoding
59         {
60                 public ISO2022JPEncoding (int codePage, bool allow1ByteKana, bool allowShiftIO)
61                         : base (codePage, 932)
62                 {
63                         this.allow_1byte_kana = allow1ByteKana;
64                         this.allow_shift_io = allowShiftIO;
65                 }
66
67                 readonly bool allow_1byte_kana, allow_shift_io;
68
69                 public override string BodyName {
70                         get { return "iso-2022-jp"; }
71                 }
72
73                 public override string HeaderName {
74                         get { return "iso-2022-jp"; }
75                 }
76
77                 public override string WebName {
78                         get { return "csISO2022JP"; }
79                 }
80
81                 public override int GetMaxByteCount (int charCount)
82                 {
83                         // ESC w ESC s ESC w ... (even number) ESC s
84                         return charCount / 2 * 5 + 4;
85                 }
86
87                 public override int GetMaxCharCount (int byteCount)
88                 {
89                         // no escape sequence
90                         return byteCount;
91                 }
92
93 #if !DISABLE_UNSAFE
94                 protected override unsafe int GetBytesInternal(char* chars, int charCount, byte* bytes, int byteCount, bool flush, object state)
95                 {
96                         if (state != null)
97                                 return ((ISO2022JPEncoder)state).GetBytesImpl (chars, charCount, bytes, byteCount, true);
98
99                         return new ISO2022JPEncoder (this, allow_1byte_kana, allow_shift_io).GetBytesImpl (chars, charCount, bytes, byteCount, true);
100                 }
101
102                 public unsafe override int GetByteCountImpl (char* chars, int count)
103                 {
104                         return new ISO2022JPEncoder (this, allow_1byte_kana, allow_shift_io).GetByteCountImpl (chars, count, true);
105                 }
106
107                 public unsafe override int GetBytesImpl (char* chars, int charCount, byte* bytes, int byteCount)
108                 {
109                         return new ISO2022JPEncoder (this, allow_1byte_kana, allow_shift_io).GetBytesImpl (chars, charCount, bytes, byteCount, true);
110                 }
111 #else
112                 protected override int GetBytesInternal(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, bool flush, object state)
113                 {
114                         if (state != null)
115                                 return ((ISO2022JPEncoder)state).GetBytesInternal(chars, charIndex, charCount, bytes, byteIndex, true);
116
117                         return new ISO2022JPEncoder(this, allow_1byte_kana, allow_shift_io).GetBytesInternal(chars, charIndex, charCount, bytes, byteIndex, true);
118                 }
119
120                 public override int GetByteCount(char[] chars, int charIndex, int charCount)
121                 {
122                         return new ISO2022JPEncoder(this, allow_1byte_kana, allow_shift_io).GetByteCount(chars, charIndex, charCount, true);
123                 }
124
125                 public override int  GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
126                 {
127                         return new ISO2022JPEncoder (this, allow_1byte_kana, allow_shift_io).GetBytes(chars, charIndex, charCount, bytes, byteIndex, true);
128                 }
129 #endif
130
131                 public override int GetCharCount (byte [] bytes, int index, int count)
132                 {
133                         return new ISO2022JPDecoder (allow_1byte_kana, allow_shift_io).GetCharCount (bytes, index, count);
134                 }
135
136                 public override int GetChars (byte [] bytes, int byteIndex, int byteCount, char [] chars, int charIndex)
137                 {
138                         return new ISO2022JPDecoder (allow_1byte_kana, allow_shift_io).GetChars (bytes, byteIndex, byteCount, chars, charIndex);
139                 }
140         }
141
142         internal enum ISO2022JPMode {
143                 ASCII,
144                 JISX0208,
145                 JISX0201
146         }
147
148         internal class ISO2022JPEncoder : MonoEncoder
149         {
150                 static JISConvert convert = JISConvert.Convert;
151
152                 readonly bool allow_1byte_kana, allow_shift_io;
153
154                 ISO2022JPMode m = ISO2022JPMode.ASCII;
155                 bool shifted_in_count, shifted_in_conv;
156
157                 public ISO2022JPEncoder(MonoEncoding owner, bool allow1ByteKana, bool allowShiftIO)
158                         : base (owner)
159                 {
160                         this.allow_1byte_kana = allow1ByteKana;
161                         this.allow_shift_io = allowShiftIO;
162                 }
163
164 #if !DISABLE_UNSAFE
165                 public unsafe override int GetByteCountImpl (char* chars, int charCount, bool flush)
166                 {
167                         return GetBytesImpl(chars, charCount, null, 0, flush);
168                 }
169 #else
170                 public override int GetByteCount(char[] chars, int charIndex, int charCount, bool flush)
171                 {
172                         return GetBytesInternal (chars, charIndex, charCount, null, 0, true);
173                 }
174 #endif
175
176 #if !DISABLE_UNSAFE
177                 private unsafe bool IsShifted(byte *bytes)
178                 {
179                         return bytes == null ? shifted_in_count : shifted_in_conv;
180                 }
181
182                 private unsafe void SetShifted(byte *bytes, bool state)
183                 {
184                         if (bytes == null)
185                                 shifted_in_count = state;
186                         else
187                                 shifted_in_conv = state;
188                 }
189
190                 // returns false if it failed to add required ESC.
191                 private unsafe void SwitchMode (byte* bytes, ref int byteIndex,
192                         ref int byteCount, ref ISO2022JPMode cur, ISO2022JPMode next)
193                 {
194                         if (cur == next)
195                                 return;
196
197                         // If bytes == null we are just counting chars..
198                         if (bytes == null) {
199                                 byteIndex += 3;
200                                 cur = next;
201                                 return;
202                         }
203
204                         if (byteCount <= 3)
205                                 throw new ArgumentOutOfRangeException ("Insufficient byte buffer.");
206
207                         bytes [byteIndex++] = 0x1B;
208                         switch (next) {
209                         case ISO2022JPMode.JISX0201:
210                                 bytes [byteIndex++] = 0x28;
211                                 bytes [byteIndex++] = 0x49;
212                                 break;
213                         case ISO2022JPMode.JISX0208:
214                                 bytes [byteIndex++] = 0x24;
215                                 bytes [byteIndex++] = 0x42;
216                                 break;
217                         default:
218                                 bytes [byteIndex++] = 0x28;
219                                 bytes [byteIndex++] = 0x42;
220                                 break;
221                         }
222                         cur = next;
223                 }
224 #else
225                 private bool IsShifted(byte[] bytes)
226                 {
227                         return bytes == null ? shifted_in_count : shifted_in_conv;
228                 }
229
230                 private void SetShifted(byte[] bytes, bool state)
231                 {
232                         if (bytes == null)
233                                 shifted_in_count = state;
234                         else
235                                 shifted_in_conv = state;
236                 }
237
238                 private void SwitchMode(byte[] bytes, ref int byteIndex,
239                         ref int byteCount, ref ISO2022JPMode cur, ISO2022JPMode next)
240                 {
241                         if (cur == next)
242                                 return;
243
244                         // If bytes == null we are just counting chars..
245                         if (bytes == null)
246                         {
247                                 byteIndex += 3;
248                                 cur = next;
249                                 return;
250                         }
251
252                         if (byteCount <= 3)
253                                 throw new ArgumentOutOfRangeException("Insufficient byte buffer.");
254
255                         bytes[byteIndex++] = 0x1B;
256                         switch (next)
257                         {
258                                 case ISO2022JPMode.JISX0201:
259                                         bytes[byteIndex++] = 0x28;
260                                         bytes[byteIndex++] = 0x49;
261                                         break;
262                                 case ISO2022JPMode.JISX0208:
263                                         bytes[byteIndex++] = 0x24;
264                                         bytes[byteIndex++] = 0x42;
265                                         break;
266                                 default:
267                                         bytes[byteIndex++] = 0x28;
268                                         bytes[byteIndex++] = 0x42;
269                                         break;
270                         }
271
272                         cur = next;
273                 }
274 #endif
275
276                 static readonly char [] full_width_map = new char [] {
277                         '\0', '\u3002', '\u300C', '\u300D', '\u3001', '\u30FB', // to nakaguro
278                         '\u30F2', '\u30A1', '\u30A3', '\u30A5', '\u30A7', '\u30A9', '\u30E3', '\u30E5', '\u30E7', '\u30C3', // to small tsu
279                         '\u30FC', '\u30A2', '\u30A4', '\u30A6', '\u30A8', '\u30AA', // A-O
280                         '\u30AB', '\u30AD', '\u30AF', '\u30B1', '\u30B3',
281                         '\u30B5', '\u30B7', '\u30B9', '\u30BB', '\u30BD',
282                         '\u30BF', '\u30C1', '\u30C4', '\u30C6', '\u30C8',
283                         '\u30CA', '\u30CB', '\u30CC', '\u30CD', '\u30CE',
284                         '\u30CF', '\u30D2', '\u30D5', '\u30D8', '\u30DB',
285                         '\u30DE', '\u30DF', '\u30E0', '\u30E1', '\u30E2',
286                         '\u30E4', '\u30E6', '\u30E8', // Ya-Yo
287                         '\u30E9', '\u30EA', '\u30EB', '\u30EC', '\u30ED',
288                         '\u30EF', '\u30F3', '\u309B', '\u309C' };
289
290 #if !DISABLE_UNSAFE
291                 public unsafe override int GetBytesImpl (
292                         char* chars, int charCount,
293                         byte* bytes, int byteCount, bool flush)
294                 {
295                         int charIndex = 0;
296                         int byteIndex = 0;
297
298                         int start = byteIndex;
299                         int end = charIndex + charCount;
300                         int value;
301
302                         for (int i = charIndex; i < end; i++, charCount--) {
303                                 char ch = chars [i];
304
305                                 // When half-kana is not allowed and it is
306                                 // actually in the input, convert to full width
307                                 // kana.
308                                 if (!allow_1byte_kana &&
309                                         ch >= 0xFF60 && ch <= 0xFFA0)
310                                         ch = full_width_map [ch - 0xFF60];
311
312                                 if (ch >= 0x2010 && ch <= 0x9FA5)
313                                 {
314                                         if (IsShifted(bytes)) {
315                                                 var offset = byteIndex++;
316                                                 if (bytes != null) bytes [offset] = 0x0F;
317                                                 SetShifted(bytes, false);
318                                                 byteCount--;
319                                         }
320                                         switch (m) {
321                                         case ISO2022JPMode.JISX0208:
322                                                 break;
323                                         default:
324                                                 SwitchMode (bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0208);
325                                                 break;
326                                         }
327                                         // This range contains the bulk of the CJK set.
328                                         value = (ch - 0x2010) * 2;
329                                         value = ((int)(convert.cjkToJis[value])) |
330                                                         (((int)(convert.cjkToJis[value + 1])) << 8);
331                                 } else if (ch >= 0xFF01 && ch <= 0xFF60) {
332                                         if (IsShifted(bytes)) {
333                                                 var offset = byteIndex++;
334                                                 if (bytes != null) bytes [offset] = 0x0F;
335                                                 SetShifted(bytes, false);
336                                                 byteCount--;
337                                         }
338                                         switch (m) {
339                                         case ISO2022JPMode.JISX0208:
340                                                 break;
341                                         default:
342                                                 SwitchMode (bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0208);
343                                                 break;
344                                         }
345
346                                         // This range contains extra characters,
347                                         value = (ch - 0xFF01) * 2;
348                                         value = ((int)(convert.extraToJis[value])) |
349                                                         (((int)(convert.extraToJis[value + 1])) << 8);
350                                 } else if (ch >= 0xFF60 && ch <= 0xFFA0) {
351                                         // disallowed half-width kana is
352                                         // already converted to full-width kana
353                                         // so here we don't have to consider it.
354
355                                         if (allow_shift_io) {
356                                                 if (!IsShifted(bytes)) {
357                                                         var offset = byteIndex++;
358                                                         if (bytes != null) bytes [offset] = 0x0E;
359                                                         SetShifted(bytes, true);
360                                                         byteCount--;
361                                                 }
362                                         } else {
363                                                 switch (m) {
364                                                 case ISO2022JPMode.JISX0201:
365                                                         break;
366                                                 default:
367                                                         SwitchMode (bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0201);
368                                                         break;
369                                                 }
370                                         }
371                                         value = ch - 0xFF40;
372                                 } else if (ch < 128) {
373                                         if (IsShifted(bytes)) {
374                                                 var offset = byteIndex++;
375                                                 if (bytes != null) bytes [offset] = 0x0F;
376                                                 SetShifted(bytes, false);
377                                                 byteCount--;
378                                         }
379                                         SwitchMode (bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.ASCII);
380                                         value = (int) ch;
381                                 } else {
382 #if NET_2_0
383                                         HandleFallback (
384                                                 chars, ref i, ref charCount,
385                                                 bytes, ref byteIndex, ref byteCount, this);
386 #endif
387                                         // skip non-convertible character
388                                         continue;
389                                 }
390
391 //Console.WriteLine ("{0:X04} : {1:x02} {2:x02}", v, (int) v / 94 + 33, v % 94 + 33);
392                                 if (value >= 0x100) {
393                                         value -= 0x0100;
394                                         if (bytes != null) {
395                                                 bytes [byteIndex++] = (byte) (value / 94 + 33);
396                                                 bytes [byteIndex++] = (byte) (value % 94 + 33);
397                                         } else {
398                                                 byteIndex += 2;
399                                         }
400                                         byteCount -= 2;
401                                 }
402                                 else {
403                                         var offset = byteIndex++;
404                                         if (bytes != null) bytes [offset] = (byte) value;
405                                         byteCount--;
406                                 }
407                         }
408                         if (flush) {
409                                 // must end in ASCII mode
410                                 if (IsShifted(bytes)) {
411                                         var offset = byteIndex++;
412                                         if (bytes != null) bytes [offset] = 0x0F;
413                                         SetShifted(bytes, false);
414                                         byteCount--;
415                                 }
416                                 if (m != ISO2022JPMode.ASCII)
417                                         SwitchMode (bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.ASCII);
418                         }
419                         return byteIndex - start;
420                 }
421 #else
422                 internal int GetBytesInternal(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, bool flush)
423                 {
424                         int start = byteIndex;
425                         int end = charIndex + charCount;
426                         int value;
427                         int byteCount = bytes != null ? bytes.Length : 0;
428
429                         for (int i = charIndex; i < end; i++, charCount--)
430                         {
431                                 char ch = chars[i];
432
433                                 // When half-kana is not allowed and it is
434                                 // actually in the input, convert to full width
435                                 // kana.
436                                 if (!allow_1byte_kana &&
437                                         ch >= 0xFF60 && ch <= 0xFFA0)
438                                         ch = full_width_map[ch - 0xFF60];
439
440                                 if (ch >= 0x2010 && ch <= 0x9FA5)
441                                 {
442                                         if (IsShifted (bytes))
443                                         {
444                                                 var offset = byteIndex++;
445                                                 if (bytes != null) bytes[offset] = 0x0F;
446                                                 SetShifted (bytes, false);
447                                                 byteCount--;
448                                         }
449                                         switch (m)
450                                         {
451                                                 case ISO2022JPMode.JISX0208:
452                                                         break;
453                                                 default:
454                                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0208);
455                                                         break;
456                                         }
457                                         // This range contains the bulk of the CJK set.
458                                         value = (ch - 0x2010) * 2;
459                                         value = ((int)(convert.cjkToJis[value])) |
460                                                         (((int)(convert.cjkToJis[value + 1])) << 8);
461                                 }
462                                 else if (ch >= 0xFF01 && ch <= 0xFF60)
463                                 {
464                                         if (IsShifted(bytes))
465                                         {
466                                                 var offset = byteIndex++;
467                                                 if (bytes != null) bytes[offset] = 0x0F;
468                                                 SetShifted (bytes, false);
469                                                 byteCount--;
470                                         }
471                                         switch (m)
472                                         {
473                                                 case ISO2022JPMode.JISX0208:
474                                                         break;
475                                                 default:
476                                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0208);
477                                                         break;
478                                         }
479
480                                         // This range contains extra characters,
481                                         value = (ch - 0xFF01) * 2;
482                                         value = ((int)(convert.extraToJis[value])) |
483                                                         (((int)(convert.extraToJis[value + 1])) << 8);
484                                 }
485                                 else if (ch >= 0xFF60 && ch <= 0xFFA0)
486                                 {
487                                         // disallowed half-width kana is
488                                         // already converted to full-width kana
489                                         // so here we don't have to consider it.
490
491                                         if (allow_shift_io)
492                                         {
493                                                 if (!IsShifted (bytes))
494                                                 {
495                                                         var offset = byteIndex++;
496                                                         if (bytes != null) bytes[offset] = 0x0E;
497                                                         SetShifted (bytes, true);
498                                                         byteCount--;
499                                                 }
500                                         }
501                                         else
502                                         {
503                                                 switch (m)
504                                                 {
505                                                         case ISO2022JPMode.JISX0201:
506                                                                 break;
507                                                         default:
508                                                                 SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0201);
509                                                                 break;
510                                                 }
511                                         }
512                                         value = ch - 0xFF40;
513                                 }
514                                 else if (ch < 128)
515                                 {
516                                         if (IsShifted (bytes))
517                                         {
518                                                 var offset = byteIndex++;
519                                                 if (bytes != null) bytes[offset] = 0x0F;
520                                                 SetShifted (bytes, false);
521                                                 byteCount--;
522                                         }
523                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.ASCII);
524                                         value = (int)ch;
525                                 }
526                                 else
527                                 {
528 #if NET_2_0
529                                         HandleFallback (chars, ref i, ref charCount,
530                                                 bytes, ref byteIndex, ref byteCount, this);
531 #endif
532                                         // skip non-convertible character
533                                         continue;
534                                 }
535
536                                 //Console.WriteLine ("{0:X04} : {1:x02} {2:x02}", v, (int) v / 94 + 33, v % 94 + 33);
537                                 if (value >= 0x100)
538                                 {
539                                         value -= 0x0100;
540                                         if (bytes != null)
541                                         {
542                                                 bytes[byteIndex++] = (byte)(value / 94 + 33);
543                                                 bytes[byteIndex++] = (byte)(value % 94 + 33);
544                                         }
545                                         else
546                                         {
547                                                 byteIndex += 2;
548                                         }
549                                         byteCount -= 2;
550                                 }
551                                 else
552                                 {
553                                         var offset = byteIndex++;
554                                         if (bytes != null) bytes[offset] = (byte)value;
555                                         byteCount--;
556                                 }
557                         }
558                         if (flush)
559                         {
560                                 // must end in ASCII mode
561                                 if (IsShifted (bytes))
562                                 {
563                                         var offset = byteIndex++;
564                                         if (bytes != null) bytes[offset] = 0x0F;
565                                         SetShifted (bytes, false);
566                                         byteCount--;
567                                 }
568                                 if (m != ISO2022JPMode.ASCII)
569                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.ASCII);
570                         }
571
572                         return byteIndex - start;
573                 }
574                 
575                 public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, bool flush)
576                 {
577                         return GetBytesInternal (chars, charIndex, charCount, bytes, byteIndex, flush);
578                 }
579 #endif
580
581 #if NET_2_0
582                 public override void Reset ()
583                 {
584                         m = ISO2022JPMode.ASCII;
585                         shifted_in_conv = shifted_in_count = false;
586                 }
587 #endif
588         }
589
590
591         internal class ISO2022JPDecoder : Decoder
592         {
593                 static JISConvert convert = JISConvert.Convert;
594
595                 readonly bool allow_shift_io;
596                 ISO2022JPMode m = ISO2022JPMode.ASCII;
597                 bool shifted_in_conv, shifted_in_count;
598
599                 public ISO2022JPDecoder (bool allow1ByteKana, bool allowShiftIO)
600                 {
601                         this.allow_shift_io = allowShiftIO;
602                 }
603
604                 // GetCharCount
605                 public override int GetCharCount (byte [] bytes, int index, int count)
606                 {
607                         int ret = 0;
608
609                         int end = index + count;
610                         for (int i = index; i < end; i++) {
611                                 if (allow_shift_io) {
612                                         switch (bytes [i]) {
613                                         case 0x0F:
614                                                 shifted_in_count = false;
615                                                 continue;
616                                         case 0x0E:
617                                                 shifted_in_count = true;
618                                                 continue;
619                                         }
620                                 }
621                                 if (bytes [i] != 0x1B) {
622                                         if (!shifted_in_count && m == ISO2022JPMode.JISX0208) {
623                                                 if (i + 1 == end)
624                                                         break; // incomplete head of wide char
625                                                 else
626                                                         ret++;
627                                                 i++; // 2 byte char
628                                         }
629                                         else
630                                                 ret++; // half-kana or ASCII
631                                 } else {
632                                         if (i + 2 >= end)
633                                                 break; // incomplete escape sequence
634                                         i++;
635                                         bool wide = false;
636                                         if (bytes [i] == 0x24)
637                                                 wide = true;
638                                         else if (bytes [i] == 0x28)
639                                                 wide = false;
640                                         else {
641                                                 ret += 2;
642                                                 continue;
643                                         }
644                                         i++;
645                                         if (bytes [i] == 0x42 || bytes [i] == 0x40)
646                                                 m = wide ? ISO2022JPMode.JISX0208 : ISO2022JPMode.ASCII;
647                                         else if (bytes [i] == 0x4A) // obsoleted
648                                                 m = ISO2022JPMode.ASCII;
649                                         else if (bytes [i] == 0x49)
650                                                 m = ISO2022JPMode.JISX0201;
651                                         else
652                                                 ret += 3;
653                                 }
654                         }
655                         return ret;
656                 }
657
658                 private int ToChar (int value)
659                 {
660                         value <<= 1;
661                         return value + 1 >= convert.jisx0208ToUnicode.Length  || value < 0 ?
662                                 -1 :
663                                 ((int) (convert.jisx0208ToUnicode [value])) |
664                                         (((int) (convert.jisx0208ToUnicode [value + 1])) << 8);
665                 }
666
667                 public override int GetChars (byte [] bytes, int byteIndex, int byteCount, char [] chars, int charIndex)
668                 {
669                         int start = charIndex;
670                         int end = byteIndex + byteCount;
671                         for (int i = byteIndex; i < end && charIndex < chars.Length; i++) {
672                                 if (allow_shift_io) {
673                                         switch (bytes [i]) {
674                                         case 0x0F:
675                                                 shifted_in_conv = false;
676                                                 continue;
677                                         case 0x0E:
678                                                 shifted_in_conv = true;
679                                                 continue;
680                                         }
681                                 }
682
683                                 if (bytes [i] != 0x1B) {
684                                         if (shifted_in_conv || m == ISO2022JPMode.JISX0201) {
685                                                 // half-kana
686                                                 if (bytes [i] < 0x60)
687                                                         chars [charIndex++] = (char) (bytes [i] + 0xFF40);
688                                                 else
689                                                         // invalid
690                                                         chars [charIndex++] = '?';
691                                         }
692                                         else if (m == ISO2022JPMode.JISX0208) {
693                                                 if (i + 1 == end)
694                                                         break; // incomplete head of wide char
695
696                                                 // am so lazy, so reusing jis2sjis
697                                                 int s1 = ((bytes [i] - 1) >> 1) + ((bytes [i] <= 0x5e) ? 0x71 : 0xb1);
698                                                 int s2 = bytes [i + 1] + (((bytes [i] & 1) != 0) ? 0x20 : 0x7e);
699                                                 int v = (s1 - 0x81) * 0xBC;
700                                                 v += s2 - 0x41;
701
702                                                 int ch = ToChar (v);
703                                                 if (ch < 0)
704                                                         chars [charIndex++] = '?';
705                                                 else
706                                                         chars [charIndex++] = (char) ch;
707                                                 i++;
708                                         }
709                                         // LAMESPEC: actually this should not
710                                         // be allowed when 1byte-kana is not
711                                         // allowed, but MS.NET seems to allow
712                                         // it in any mode.
713                                         else if (bytes [i] > 0xA0 && bytes [i] < 0xE0) // half-width Katakana
714                                                 chars [charIndex++] = (char) (bytes [i] - 0xA0 + 0xFF60);
715                                         else
716                                                 chars [charIndex++] = (char) bytes [i];
717                                         continue;
718                                 } else {
719                                         if (i + 2 >= end)
720                                                 break; // incomplete escape sequence
721                                         i++;
722                                         bool wide = false;
723                                         if (bytes [i] == 0x24)
724                                                 wide = true;
725                                         else if (bytes [i] == 0x28)
726                                                 wide = false;
727                                         else {
728                                                 chars [charIndex++] = '\x1B';
729                                                 chars [charIndex++] = (char) bytes [i];
730                                                 continue;
731                                         }
732                                         i++;
733                                         if (bytes [i] == 0x42 || bytes [i] == 0x40)
734                                                 m = wide ? ISO2022JPMode.JISX0208 : ISO2022JPMode.ASCII;
735                                         else if (bytes [i] == 0x4A) // obsoleted
736                                                 m = ISO2022JPMode.ASCII;
737                                         else if (bytes [i] == 0x49)
738                                                 m = ISO2022JPMode.JISX0201;
739                                         else {
740                                                 chars [charIndex++] = '\x1B';
741                                                 chars [charIndex++] = (char) bytes [i - 1];
742                                                 chars [charIndex++] = (char) bytes [i];
743                                         }
744                                 }
745                         }
746
747                         return charIndex - start;
748                 }
749
750 #if NET_2_0
751                 public override void Reset ()
752                 {
753                         m = ISO2022JPMode.ASCII;
754                         shifted_in_count = shifted_in_conv = false;
755                 }
756 #endif
757         }
758
759         [Serializable]
760         public class ENCiso_2022_jp : CP50220
761         {
762                 public ENCiso_2022_jp () : base() {}
763
764         }; // class ENCiso_2022_jp
765 }