753b2a36b14bdea8a29b5da3bfbad8f556803c47
[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                                         HandleFallback (
383                                                 chars, ref i, ref charCount,
384                                                 bytes, ref byteIndex, ref byteCount, this);
385                                         // skip non-convertible character
386                                         continue;
387                                 }
388
389 //Console.WriteLine ("{0:X04} : {1:x02} {2:x02}", v, (int) v / 94 + 33, v % 94 + 33);
390                                 if (value >= 0x100) {
391                                         value -= 0x0100;
392                                         if (bytes != null) {
393                                                 bytes [byteIndex++] = (byte) (value / 94 + 33);
394                                                 bytes [byteIndex++] = (byte) (value % 94 + 33);
395                                         } else {
396                                                 byteIndex += 2;
397                                         }
398                                         byteCount -= 2;
399                                 }
400                                 else {
401                                         var offset = byteIndex++;
402                                         if (bytes != null) bytes [offset] = (byte) value;
403                                         byteCount--;
404                                 }
405                         }
406                         if (flush) {
407                                 // must end in ASCII mode
408                                 if (IsShifted(bytes)) {
409                                         var offset = byteIndex++;
410                                         if (bytes != null) bytes [offset] = 0x0F;
411                                         SetShifted(bytes, false);
412                                         byteCount--;
413                                 }
414                                 if (m != ISO2022JPMode.ASCII)
415                                         SwitchMode (bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.ASCII);
416                         }
417                         return byteIndex - start;
418                 }
419 #else
420                 internal int GetBytesInternal(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, bool flush)
421                 {
422                         int start = byteIndex;
423                         int end = charIndex + charCount;
424                         int value;
425                         int byteCount = bytes != null ? bytes.Length : 0;
426
427                         for (int i = charIndex; i < end; i++, charCount--)
428                         {
429                                 char ch = chars[i];
430
431                                 // When half-kana is not allowed and it is
432                                 // actually in the input, convert to full width
433                                 // kana.
434                                 if (!allow_1byte_kana &&
435                                         ch >= 0xFF60 && ch <= 0xFFA0)
436                                         ch = full_width_map[ch - 0xFF60];
437
438                                 if (ch >= 0x2010 && ch <= 0x9FA5)
439                                 {
440                                         if (IsShifted (bytes))
441                                         {
442                                                 var offset = byteIndex++;
443                                                 if (bytes != null) bytes[offset] = 0x0F;
444                                                 SetShifted (bytes, false);
445                                                 byteCount--;
446                                         }
447                                         switch (m)
448                                         {
449                                                 case ISO2022JPMode.JISX0208:
450                                                         break;
451                                                 default:
452                                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0208);
453                                                         break;
454                                         }
455                                         // This range contains the bulk of the CJK set.
456                                         value = (ch - 0x2010) * 2;
457                                         value = ((int)(convert.cjkToJis[value])) |
458                                                         (((int)(convert.cjkToJis[value + 1])) << 8);
459                                 }
460                                 else if (ch >= 0xFF01 && ch <= 0xFF60)
461                                 {
462                                         if (IsShifted(bytes))
463                                         {
464                                                 var offset = byteIndex++;
465                                                 if (bytes != null) bytes[offset] = 0x0F;
466                                                 SetShifted (bytes, false);
467                                                 byteCount--;
468                                         }
469                                         switch (m)
470                                         {
471                                                 case ISO2022JPMode.JISX0208:
472                                                         break;
473                                                 default:
474                                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0208);
475                                                         break;
476                                         }
477
478                                         // This range contains extra characters,
479                                         value = (ch - 0xFF01) * 2;
480                                         value = ((int)(convert.extraToJis[value])) |
481                                                         (((int)(convert.extraToJis[value + 1])) << 8);
482                                 }
483                                 else if (ch >= 0xFF60 && ch <= 0xFFA0)
484                                 {
485                                         // disallowed half-width kana is
486                                         // already converted to full-width kana
487                                         // so here we don't have to consider it.
488
489                                         if (allow_shift_io)
490                                         {
491                                                 if (!IsShifted (bytes))
492                                                 {
493                                                         var offset = byteIndex++;
494                                                         if (bytes != null) bytes[offset] = 0x0E;
495                                                         SetShifted (bytes, true);
496                                                         byteCount--;
497                                                 }
498                                         }
499                                         else
500                                         {
501                                                 switch (m)
502                                                 {
503                                                         case ISO2022JPMode.JISX0201:
504                                                                 break;
505                                                         default:
506                                                                 SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.JISX0201);
507                                                                 break;
508                                                 }
509                                         }
510                                         value = ch - 0xFF40;
511                                 }
512                                 else if (ch < 128)
513                                 {
514                                         if (IsShifted (bytes))
515                                         {
516                                                 var offset = byteIndex++;
517                                                 if (bytes != null) bytes[offset] = 0x0F;
518                                                 SetShifted (bytes, false);
519                                                 byteCount--;
520                                         }
521                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.ASCII);
522                                         value = (int)ch;
523                                 }
524                                 else
525                                 {
526                                         HandleFallback (chars, ref i, ref charCount,
527                                                 bytes, ref byteIndex, ref byteCount, this);
528                                         // skip non-convertible character
529                                         continue;
530                                 }
531
532                                 //Console.WriteLine ("{0:X04} : {1:x02} {2:x02}", v, (int) v / 94 + 33, v % 94 + 33);
533                                 if (value >= 0x100)
534                                 {
535                                         value -= 0x0100;
536                                         if (bytes != null)
537                                         {
538                                                 bytes[byteIndex++] = (byte)(value / 94 + 33);
539                                                 bytes[byteIndex++] = (byte)(value % 94 + 33);
540                                         }
541                                         else
542                                         {
543                                                 byteIndex += 2;
544                                         }
545                                         byteCount -= 2;
546                                 }
547                                 else
548                                 {
549                                         var offset = byteIndex++;
550                                         if (bytes != null) bytes[offset] = (byte)value;
551                                         byteCount--;
552                                 }
553                         }
554                         if (flush)
555                         {
556                                 // must end in ASCII mode
557                                 if (IsShifted (bytes))
558                                 {
559                                         var offset = byteIndex++;
560                                         if (bytes != null) bytes[offset] = 0x0F;
561                                         SetShifted (bytes, false);
562                                         byteCount--;
563                                 }
564                                 if (m != ISO2022JPMode.ASCII)
565                                         SwitchMode(bytes, ref byteIndex, ref byteCount, ref m, ISO2022JPMode.ASCII);
566                         }
567
568                         return byteIndex - start;
569                 }
570                 
571                 public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, bool flush)
572                 {
573                         return GetBytesInternal (chars, charIndex, charCount, bytes, byteIndex, flush);
574                 }
575 #endif
576
577                 public override void Reset ()
578                 {
579                         m = ISO2022JPMode.ASCII;
580                         shifted_in_conv = shifted_in_count = false;
581                 }
582         }
583
584
585         internal class ISO2022JPDecoder : Decoder
586         {
587                 static JISConvert convert = JISConvert.Convert;
588
589                 readonly bool allow_shift_io;
590                 ISO2022JPMode m = ISO2022JPMode.ASCII;
591                 bool shifted_in_conv, shifted_in_count;
592
593                 public ISO2022JPDecoder (bool allow1ByteKana, bool allowShiftIO)
594                 {
595                         this.allow_shift_io = allowShiftIO;
596                 }
597
598                 // GetCharCount
599                 public override int GetCharCount (byte [] bytes, int index, int count)
600                 {
601                         int ret = 0;
602
603                         int end = index + count;
604                         for (int i = index; i < end; i++) {
605                                 if (allow_shift_io) {
606                                         switch (bytes [i]) {
607                                         case 0x0F:
608                                                 shifted_in_count = false;
609                                                 continue;
610                                         case 0x0E:
611                                                 shifted_in_count = true;
612                                                 continue;
613                                         }
614                                 }
615                                 if (bytes [i] != 0x1B) {
616                                         if (!shifted_in_count && m == ISO2022JPMode.JISX0208) {
617                                                 if (i + 1 == end)
618                                                         break; // incomplete head of wide char
619                                                 else
620                                                         ret++;
621                                                 i++; // 2 byte char
622                                         }
623                                         else
624                                                 ret++; // half-kana or ASCII
625                                 } else {
626                                         if (i + 2 >= end)
627                                                 break; // incomplete escape sequence
628                                         i++;
629                                         bool wide = false;
630                                         if (bytes [i] == 0x24)
631                                                 wide = true;
632                                         else if (bytes [i] == 0x28)
633                                                 wide = false;
634                                         else {
635                                                 ret += 2;
636                                                 continue;
637                                         }
638                                         i++;
639                                         if (bytes [i] == 0x42 || bytes [i] == 0x40)
640                                                 m = wide ? ISO2022JPMode.JISX0208 : ISO2022JPMode.ASCII;
641                                         else if (bytes [i] == 0x4A) // obsoleted
642                                                 m = ISO2022JPMode.ASCII;
643                                         else if (bytes [i] == 0x49)
644                                                 m = ISO2022JPMode.JISX0201;
645                                         else
646                                                 ret += 3;
647                                 }
648                         }
649                         return ret;
650                 }
651
652                 private int ToChar (int value)
653                 {
654                         value <<= 1;
655                         return value + 1 >= convert.jisx0208ToUnicode.Length  || value < 0 ?
656                                 -1 :
657                                 ((int) (convert.jisx0208ToUnicode [value])) |
658                                         (((int) (convert.jisx0208ToUnicode [value + 1])) << 8);
659                 }
660
661                 public override int GetChars (byte [] bytes, int byteIndex, int byteCount, char [] chars, int charIndex)
662                 {
663                         int start = charIndex;
664                         int end = byteIndex + byteCount;
665                         for (int i = byteIndex; i < end && charIndex < chars.Length; i++) {
666                                 if (allow_shift_io) {
667                                         switch (bytes [i]) {
668                                         case 0x0F:
669                                                 shifted_in_conv = false;
670                                                 continue;
671                                         case 0x0E:
672                                                 shifted_in_conv = true;
673                                                 continue;
674                                         }
675                                 }
676
677                                 if (bytes [i] != 0x1B) {
678                                         if (shifted_in_conv || m == ISO2022JPMode.JISX0201) {
679                                                 // half-kana
680                                                 if (bytes [i] < 0x60)
681                                                         chars [charIndex++] = (char) (bytes [i] + 0xFF40);
682                                                 else
683                                                         // invalid
684                                                         chars [charIndex++] = '?';
685                                         }
686                                         else if (m == ISO2022JPMode.JISX0208) {
687                                                 if (i + 1 == end)
688                                                         break; // incomplete head of wide char
689
690                                                 // am so lazy, so reusing jis2sjis
691                                                 int s1 = ((bytes [i] - 1) >> 1) + ((bytes [i] <= 0x5e) ? 0x71 : 0xb1);
692                                                 int s2 = bytes [i + 1] + (((bytes [i] & 1) != 0) ? 0x20 : 0x7e);
693                                                 int v = (s1 - 0x81) * 0xBC;
694                                                 v += s2 - 0x41;
695
696                                                 int ch = ToChar (v);
697                                                 if (ch < 0)
698                                                         chars [charIndex++] = '?';
699                                                 else
700                                                         chars [charIndex++] = (char) ch;
701                                                 i++;
702                                         }
703                                         // LAMESPEC: actually this should not
704                                         // be allowed when 1byte-kana is not
705                                         // allowed, but MS.NET seems to allow
706                                         // it in any mode.
707                                         else if (bytes [i] > 0xA0 && bytes [i] < 0xE0) // half-width Katakana
708                                                 chars [charIndex++] = (char) (bytes [i] - 0xA0 + 0xFF60);
709                                         else
710                                                 chars [charIndex++] = (char) bytes [i];
711                                         continue;
712                                 } else {
713                                         if (i + 2 >= end)
714                                                 break; // incomplete escape sequence
715                                         i++;
716                                         bool wide = false;
717                                         if (bytes [i] == 0x24)
718                                                 wide = true;
719                                         else if (bytes [i] == 0x28)
720                                                 wide = false;
721                                         else {
722                                                 chars [charIndex++] = '\x1B';
723                                                 chars [charIndex++] = (char) bytes [i];
724                                                 continue;
725                                         }
726                                         i++;
727                                         if (bytes [i] == 0x42 || bytes [i] == 0x40)
728                                                 m = wide ? ISO2022JPMode.JISX0208 : ISO2022JPMode.ASCII;
729                                         else if (bytes [i] == 0x4A) // obsoleted
730                                                 m = ISO2022JPMode.ASCII;
731                                         else if (bytes [i] == 0x49)
732                                                 m = ISO2022JPMode.JISX0201;
733                                         else {
734                                                 chars [charIndex++] = '\x1B';
735                                                 chars [charIndex++] = (char) bytes [i - 1];
736                                                 chars [charIndex++] = (char) bytes [i];
737                                         }
738                                 }
739                         }
740
741                         return charIndex - start;
742                 }
743
744                 public override void Reset ()
745                 {
746                         m = ISO2022JPMode.ASCII;
747                         shifted_in_count = shifted_in_conv = false;
748                 }
749         }
750
751         [Serializable]
752         public class ENCiso_2022_jp : CP50220
753         {
754                 public ENCiso_2022_jp () : base() {}
755
756         }; // class ENCiso_2022_jp
757 }