1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.nio.cs.ext;
  27 
  28 import java.nio.ByteBuffer;
  29 import java.nio.CharBuffer;
  30 import java.nio.charset.Charset;
  31 import java.nio.charset.CharsetDecoder;
  32 import java.nio.charset.CharsetEncoder;
  33 import java.nio.charset.CoderResult;
  34 import java.util.Arrays;
  35 import sun.nio.cs.Surrogate;
  36 import static sun.nio.cs.CharsetMapping.*;
  37 
  38 /*
  39  * Four types of "DoubleByte" charsets are implemented in this class
  40  * (1)DoubleByte
  41  *    The "mostly widely used" multibyte charset, a combination of
  42  *    a singlebyte character set (usually the ASCII charset) and a
  43  *    doublebyte character set. The codepoint values of singlebyte
  44  *    and doublebyte don't overlap. Microsoft's multibyte charsets
  45  *    and IBM's "DBCS_ASCII" charsets, such as IBM1381, 942, 943,
  46  *    948, 949 and 950 are such charsets.
  47  *
  48  * (2)DoubleByte_EBCDIC
  49  *    IBM EBCDIC Mix multibyte charset. Use SO and SI to shift (switch)
  50  *    in and out between the singlebyte character set and doublebyte
  51  *    character set.
  52  *
  53  * (3)DoubleByte_SIMPLE_EUC
  54  *    It's a "simple" form of EUC encoding scheme, only have the
  55  *    singlebyte character set G0 and one doublebyte character set
  56  *    G1 are defined, G2 (with SS2) and G3 (with SS3) are not used.
  57  *    So it is actually the same as the "typical" type (1) mentioned
  58  *    above, except it return "malformed" for the SS2 and SS3 when
  59  *    decoding.
  60  *
  61  * (4)DoubleByte ONLY
  62  *    A "pure" doublebyte only character set. From implementation
  63  *    point of view, this is the type (1) with "decodeSingle" always
  64  *    returns unmappable.
  65  *
  66  * For simplicity, all implementations share the same decoding and
  67  * encoding data structure. 
  68  * 
  69  * Decoding:
  70  *
  71  *    char[][] b2c;
  72  *    char[] b2cSB;
  73  *    int b2Min, b2Max
  74  *
  75  *    public char decodeSingle(int b) {
  76  *        return b2cSB.[b];
  77  *    }
  78  *
  79  *    public char decodeDouble(int b1, int b2) {
  80  *        if (b2 < b2Min || b2 > b2Max)
  81  *            return UNMAPPABLE_DECODING;
  82  *         return b2c[b1][b2 - b2Min];
  83  *    }
  84  *
  85  *    (1)b2Min, b2Max are the corresponding min and max value of the
  86  *       low-half of the double-byte.
  87  *    (2)The high 8-bit/b1 of the double-byte are used to indexed into
  88  *       b2c array.
  89  *
  90  * Encoding:
  91  * 
  92  *    char[] c2b;
  93  *    char[] c2bIndex;
  94  *
  95  *    public int encodeChar(char ch) {
  96  *        return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
  97  *    }
  98  *
  99  */
 100 
 101 public class DoubleByte {
 102 
 103     public final static char[] B2C_UNMAPPABLE;
 104     static {
 105         B2C_UNMAPPABLE = new char[0x100];
 106         Arrays.fill(B2C_UNMAPPABLE, (char)UNMAPPABLE_DECODING);
 107     }
 108 
 109     public static class Decoder extends CharsetDecoder {
 110         final char[][] b2c;
 111         final char[] b2cSB;
 112         final int b2Min;
 113         final int b2Max;
 114 
 115         // for SimpleEUC override
 116         protected CoderResult crMalformedOrUnderFlow(int b) {
 117             return CoderResult.UNDERFLOW;
 118         }
 119 
 120         protected CoderResult crMalformedOrUnmappable(int b) {
 121             return CoderResult.unmappableForLength(2);
 122         }
 123 
 124         Decoder(Charset cs, float avgcpb, float maxcpb,
 125                 char[][] b2c, char[] b2cSB,
 126                 int b2Min, int b2Max) {
 127             super(cs, avgcpb, maxcpb);
 128             this.b2c = b2c;
 129             this.b2cSB = b2cSB;
 130             this.b2Min = b2Min;
 131             this.b2Max = b2Max;
 132         }
 133 
 134         Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
 135             this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max);
 136         }
 137 
 138         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
 139             byte[] sa = src.array();
 140             int sp = src.arrayOffset() + src.position();
 141             int sl = src.arrayOffset() + src.limit();
 142 
 143             char[] da = dst.array();
 144             int dp = dst.arrayOffset() + dst.position();
 145             int dl = dst.arrayOffset() + dst.limit();
 146 
 147             try {
 148                 while (sp < sl && dp < dl) {
 149                     // inline the decodeSingle/Double() for better performance
 150                     int inSize = 1;
 151                     int b1 = sa[sp] & 0xff;
 152                     char c = b2cSB[b1];
 153                     if (c == UNMAPPABLE_DECODING) {
 154                         if (sl - sp < 2)
 155                             return crMalformedOrUnderFlow(b1);
 156                         int b2 = sa[sp + 1] & 0xff;
 157                         if (b2 < b2Min || b2 > b2Max ||
 158                             (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 159                             return crMalformedOrUnmappable(b1);
 160                         }
 161                         inSize++;
 162                     }
 163                     da[dp++] = c;
 164                     sp += inSize;
 165                 }
 166                 return (sp >= sl) ? CoderResult.UNDERFLOW
 167                                   : CoderResult.OVERFLOW;
 168             } finally {
 169                 src.position(sp - src.arrayOffset());
 170                 dst.position(dp - dst.arrayOffset());
 171             }
 172         }
 173     
 174         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
 175             int mark = src.position();
 176             try {
 177                 while (src.hasRemaining() && dst.hasRemaining()) {
 178                     int b1 = src.get() & 0xff;
 179                     char c = b2cSB[b1];
 180                     int inSize = 1;
 181                     if (c == UNMAPPABLE_DECODING) {
 182                         if (src.remaining() < 1)
 183                             return crMalformedOrUnderFlow(b1);
 184                         int b2 = src.get() & 0xff;
 185                         if (b2 < b2Min || b2 > b2Max ||
 186                             (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING)
 187                             return crMalformedOrUnmappable(b1);
 188                         inSize++;
 189                     }
 190                     dst.put(c);
 191                     mark += inSize;
 192                 }
 193                 return src.hasRemaining()? CoderResult.OVERFLOW
 194                                          : CoderResult.UNDERFLOW;
 195             } finally {
 196                 src.position(mark);
 197             }
 198         }
 199     
 200         protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 201             if (src.hasArray() && dst.hasArray())
 202                 return decodeArrayLoop(src, dst);
 203             else
 204                 return decodeBufferLoop(src, dst);
 205         }
 206 
 207         // decode loops are not using decodeSingle/Double() for performance
 208         // reason.
 209         public char decodeSingle(int b) {
 210             return b2cSB[b];
 211         }
 212 
 213         public char decodeDouble(int b1, int b2) {
 214             if (b2 < b2Min || b2 > b2Max)
 215                 return UNMAPPABLE_DECODING;
 216             return  b2c[b1][b2 - b2Min];
 217         }
 218     }
 219 
 220     // IBM_EBCDIC_DBCS
 221     public static class Decoder_EBCDIC extends Decoder {
 222         private static final int SBCS = 0;
 223         private static final int DBCS = 1;
 224         private static final int SO = 0x0e;
 225         private static final int SI = 0x0f;
 226         private int  currentState;
 227 
 228         Decoder_EBCDIC(Charset cs,
 229                        char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
 230             super(cs, b2c, b2cSB, b2Min, b2Max);
 231         }
 232 
 233         protected void implReset() {
 234             currentState = SBCS;
 235         }
 236 
 237         // Check validity of dbcs ebcdic byte pair values
 238         //
 239         // First byte : 0x41 -- 0xFE
 240         // Second byte: 0x41 -- 0xFE 
 241         // Doublebyte blank: 0x4040
 242         //
 243         // The validation implementation in "old" DBCS_IBM_EBCDIC and sun.io
 244         // as
 245         //            if ((b1 != 0x40 || b2 != 0x40) &&
 246         //                (b2 < 0x41 || b2 > 0xfe)) {...}
 247         // is not correct/complete (range check for b1)
 248         //
 249         private static boolean isDoubleByte(int b1, int b2) {
 250             return (0x41 <= b1 && b1 <= 0xfe && 0x41 <= b2 && b2 <= 0xfe)
 251                    || (b1 == 0x40 && b2 == 0x40); // DBCS-HOST SPACE
 252         }
 253 
 254         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
 255             byte[] sa = src.array();
 256             int sp = src.arrayOffset() + src.position();
 257             int sl = src.arrayOffset() + src.limit();
 258             char[] da = dst.array();
 259             int dp = dst.arrayOffset() + dst.position();
 260             int dl = dst.arrayOffset() + dst.limit();
 261 
 262             try {
 263                 // don't check dp/dl together here, it's possible to
 264                 // decdoe a SO/SI without space in output buffer.
 265                 while (sp < sl) {
 266                     int b1 = sa[sp] & 0xff;
 267                     int inSize = 1;
 268                     if (b1 == SO) {  // Shift out
 269                         if (currentState != SBCS)
 270                             return CoderResult.malformedForLength(1);
 271                         else
 272                             currentState = DBCS;
 273                     } else if (b1 == SI) {
 274                         if (currentState != DBCS)
 275                             return CoderResult.malformedForLength(1);
 276                         else
 277                             currentState = SBCS;
 278                     } else {
 279                         char c =  UNMAPPABLE_DECODING;
 280                         if (currentState == SBCS) {
 281                             c = b2cSB[b1];
 282                             if (c == UNMAPPABLE_DECODING)
 283                                 return CoderResult.unmappableForLength(1);
 284                         } else {
 285                             if (sl - sp < 2)
 286                                 return CoderResult.UNDERFLOW;
 287                             int b2 = sa[sp + 1] & 0xff;
 288                             if (b2 < b2Min || b2 > b2Max ||
 289                                 (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 290                                 if (!isDoubleByte(b1, b2))
 291                                     return CoderResult.malformedForLength(2);
 292                                 return CoderResult.unmappableForLength(2);
 293                             }
 294                             inSize++;
 295                         }
 296                         if (dl - dp < 1)
 297                             return CoderResult.OVERFLOW;
 298 
 299                         da[dp++] = c;
 300                     }
 301                     sp += inSize;
 302                 }
 303                 return CoderResult.UNDERFLOW;
 304             } finally {
 305                 src.position(sp - src.arrayOffset());
 306                 dst.position(dp - dst.arrayOffset());
 307             }
 308         }
 309     
 310         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
 311             int mark = src.position();
 312             try {
 313                 while (src.hasRemaining()) {
 314                     int b1 = src.get() & 0xff;
 315                     int inSize = 1;
 316                     if (b1 == SO) {  // Shift out
 317                         if (currentState != SBCS)
 318                             return CoderResult.malformedForLength(1);
 319                         else
 320                             currentState = DBCS;
 321                     } else if (b1 == SI) {
 322                         if (currentState != DBCS)
 323                             return CoderResult.malformedForLength(1);
 324                         else
 325                             currentState = SBCS;
 326                     } else {
 327                         char c = UNMAPPABLE_DECODING;
 328                         if (currentState == SBCS) {
 329                             c = b2cSB[b1];
 330                             if (c == UNMAPPABLE_DECODING)
 331                                 return CoderResult.unmappableForLength(1);
 332                         } else {
 333                             if (src.remaining() < 1)
 334                                 return CoderResult.UNDERFLOW;
 335                             int b2 = src.get()&0xff;
 336                             if (b2 < b2Min || b2 > b2Max ||
 337                                 (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 338                                 if (!isDoubleByte(b1, b2))
 339                                     return CoderResult.malformedForLength(2);
 340                                 return CoderResult.unmappableForLength(2);
 341                             }
 342                             inSize++;
 343                         }
 344                         
 345                         if (dst.remaining() < 1)
 346                             return CoderResult.OVERFLOW;                      
 347 
 348                         dst.put(c);
 349                     }
 350                     mark += inSize;
 351                 }
 352                 return CoderResult.UNDERFLOW;
 353             } finally {
 354                 src.position(mark);
 355             }
 356         }
 357     }
 358 
 359     // EBCDIC_DBCS_ONLY
 360     public static class Decoder_EBCDIC_DBCSONLY extends Decoder {
 361         static final char[] b2cSB;
 362         static {
 363             b2cSB = new char[0x100];
 364             Arrays.fill(b2cSB, (char)UNMAPPABLE_DECODING);
 365         }
 366         Decoder_EBCDIC_DBCSONLY(Charset cs, char[][] b2c, int b2Min, int b2Max) {
 367             super(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max);
 368         }
 369     }
 370 
 371     // EUC_SIMPLE
 372     // The only thing we need to "override" is to check SS2/SS3 and
 373     // return "malformed" if found
 374     public static class Decoder_EUC_SIM extends Decoder {
 375         private final int SS2 =  0x8E;
 376         private final int SS3 =  0x8F;  
 377 
 378         Decoder_EUC_SIM(Charset cs,
 379                         char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
 380             super(cs, b2c, b2cSB, b2Min, b2Max);
 381         }
 382 
 383         // No support provided for G2/G3 for SimpleEUC
 384         protected CoderResult crMalformedOrUnderFlow(int b) {
 385             if (b == SS2 || b == SS3 )
 386                 return CoderResult.malformedForLength(1);
 387             return CoderResult.UNDERFLOW;
 388         }
 389 
 390         protected CoderResult crMalformedOrUnmappable(int b) {
 391             if (b == SS2 || b == SS3 )
 392                 return CoderResult.malformedForLength(1);
 393             return CoderResult.unmappableForLength(2);
 394         }
 395     }
 396 
 397     public static class Encoder extends CharsetEncoder {
 398         final int MAX_SINGLEBYTE = 0xff;
 399         private final char[] c2b;
 400         private final char[] c2bIndex;
 401         Surrogate.Parser sgp;
 402     
 403         Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
 404             super(cs, 2.0f, 2.0f);
 405             this.c2b = c2b;
 406             this.c2bIndex = c2bIndex;
 407         }
 408 
 409         Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) {
 410             super(cs, avg, max, repl);
 411             this.c2b = c2b;
 412             this.c2bIndex = c2bIndex;
 413         }
 414     
 415         public boolean canEncode(char c) {
 416             return encodeChar(c) != UNMAPPABLE_ENCODING;
 417         }
 418     
 419         Surrogate.Parser sgp() {
 420             if (sgp == null)
 421                 sgp = new Surrogate.Parser();
 422             return sgp;
 423         }
 424 
 425         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
 426             char[] sa = src.array();
 427             int sp = src.arrayOffset() + src.position();
 428             int sl = src.arrayOffset() + src.limit();
 429 
 430             byte[] da = dst.array();
 431             int dp = dst.arrayOffset() + dst.position();
 432             int dl = dst.arrayOffset() + dst.limit();
 433     
 434             try {
 435                 while (sp < sl) {
 436                     char c = sa[sp];
 437                     int bb = encodeChar(c);
 438                     if (bb == UNMAPPABLE_ENCODING) {
 439                         if (Surrogate.is(c)) {
 440                             if (sgp().parse(c, sa, sp, sl) < 0)
 441                                 return sgp.error();
 442                             return sgp.unmappableResult();
 443                         }
 444                         return CoderResult.unmappableForLength(1);
 445                     }
 446 
 447                     if (bb > MAX_SINGLEBYTE) {    // DoubleByte
 448                         if (dl - dp < 2)
 449                             return CoderResult.OVERFLOW;
 450                         da[dp++] = (byte)(bb >> 8);
 451                         da[dp++] = (byte)bb;
 452                     } else {                      // SingleByte
 453                         if (dl - dp < 1)
 454                             return CoderResult.OVERFLOW;
 455                         da[dp++] = (byte)bb;
 456                     }
 457 
 458                     sp++;
 459                 }
 460                 return CoderResult.UNDERFLOW;
 461             } finally {
 462                 src.position(sp - src.arrayOffset());
 463                 dst.position(dp - dst.arrayOffset());
 464             }
 465         }
 466     
 467         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
 468             int mark = src.position();
 469             try {
 470                 while (src.hasRemaining()) {
 471                     char c = src.get();
 472                     int bb = encodeChar(c);
 473                     if (bb == UNMAPPABLE_ENCODING) {
 474                         if (Surrogate.is(c)) {
 475                             if (sgp().parse(c, src) < 0)
 476                                 return sgp.error();
 477                             return sgp.unmappableResult();
 478                         }
 479                         return CoderResult.unmappableForLength(1);
 480                     }
 481                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
 482                         if (dst.remaining() < 2)
 483                             return CoderResult.OVERFLOW;
 484                         dst.put((byte)(bb >> 8));
 485                         dst.put((byte)(bb));
 486                     } else {
 487                         if (dst.remaining() < 1)
 488                         return CoderResult.OVERFLOW;
 489                         dst.put((byte)bb);
 490                     }
 491                     mark++;
 492                 }
 493                 return CoderResult.UNDERFLOW;
 494             } finally {
 495                 src.position(mark);
 496             }
 497         }
 498     
 499         protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
 500             if (src.hasArray() && dst.hasArray())
 501                 return encodeArrayLoop(src, dst);
 502             else
 503                 return encodeBufferLoop(src, dst);
 504         }
 505     
 506         public int encodeChar(char ch) {
 507             return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
 508         }
 509 
 510         // init the c2b and c2bIndex tables from b2c.
 511         static void initC2B(String[] b2c, String b2cSB, String b2cNR,  String c2bNR,
 512                             int b2Min, int b2Max,
 513                             char[] c2b, char[] c2bIndex)
 514         {
 515             Arrays.fill(c2b, (char)UNMAPPABLE_ENCODING);
 516             int off = 0x100;
 517 
 518             char[][] b2c_ca = new char[b2c.length][];
 519             char[] b2cSB_ca = null;
 520             if (b2cSB != null)
 521                 b2cSB_ca = b2cSB.toCharArray();
 522 
 523             for (int i = 0; i < b2c.length; i++) {
 524                 if (b2c[i] == null)         
 525                     continue;
 526                 b2c_ca[i] = b2c[i].toCharArray();
 527             }
 528 
 529             if (b2cNR != null) {
 530                 int j = 0;
 531                 while (j < b2cNR.length()) {
 532                     char b  = b2cNR.charAt(j++);
 533                     char c  = b2cNR.charAt(j++);
 534                     if (b < 0x100 && b2cSB_ca != null) {
 535                         if (b2cSB_ca[b] == c)
 536                             b2cSB_ca[b] = UNMAPPABLE_DECODING;
 537                     } else {
 538                         if (b2c_ca[b >> 8][(b & 0xff) - b2Min] == c)
 539                             b2c_ca[b >> 8][(b & 0xff) - b2Min] = UNMAPPABLE_DECODING;
 540                     }
 541                 }
 542             }
 543 
 544             if (b2cSB_ca != null) {      // SingleByte
 545                 for (int b = 0; b < b2cSB_ca.length; b++) {
 546                     char c = b2cSB_ca[b];
 547                     if (c == UNMAPPABLE_DECODING)
 548                         continue;
 549                     int index = c2bIndex[c >> 8];
 550                     if (index == 0) {
 551                         index = off;
 552                         off += 0x100;
 553                         c2bIndex[c >> 8] = (char)index;
 554                     }
 555                     c2b[index + (c & 0xff)] = (char)b;
 556                 }
 557             }
 558 
 559             for (int b1 = 0; b1 < b2c.length; b1++) {  // DoubleByte
 560                 char[] db = b2c_ca[b1];
 561                 if (db == null)         
 562                     continue;
 563                 for (int b2 = b2Min; b2 <= b2Max; b2++) {
 564                     char c = db[b2 - b2Min];
 565                     if (c == UNMAPPABLE_DECODING)
 566                         continue;
 567                     int index = c2bIndex[c >> 8];
 568                     if (index == 0) {
 569                         index = off;
 570                         off += 0x100;
 571                         c2bIndex[c >> 8] = (char)index;
 572                     }
 573                     c2b[index + (c & 0xff)] = (char)((b1 << 8) | b2);
 574                 }
 575             }
 576 
 577             if (c2bNR != null) {
 578                 // add c->b only nr entries
 579                 for (int i = 0; i < c2bNR.length(); i += 2) {
 580                     char b = c2bNR.charAt(i);
 581                     char c = c2bNR.charAt(i + 1);
 582                     int index = (c >> 8);
 583                     if (c2bIndex[index] == 0) {
 584                         c2bIndex[index] = (char)off;
 585                         off += 0x100;
 586                     }
 587                     index = c2bIndex[index] + (c & 0xff);
 588                     c2b[index] = b;
 589                 }
 590             }
 591         }
 592     }
 593 
 594     // EBCDIC_DBCS_ONLY
 595     public static class Encoder_EBCDIC_DBCSONLY extends Encoder {
 596         Encoder_EBCDIC_DBCSONLY(Charset cs, byte[] repl,
 597                                 char[] c2b, char[] c2bIndex) {
 598             super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex);
 599         }
 600 
 601         public int encodeChar(char ch) {
 602             int bb = super.encodeChar(ch);
 603             if (bb <= MAX_SINGLEBYTE)
 604                 return UNMAPPABLE_ENCODING;
 605             return bb;
 606         }
 607     }
 608 
 609     // for IBM_EBCDIC_DBCS
 610     public static class Encoder_EBCDIC extends Encoder {
 611         static final int SBCS = 0;
 612         static final int DBCS = 1;
 613         static final byte SO = 0x0e;
 614         static final byte SI = 0x0f;
 615 
 616         protected int  currentState = SBCS;
 617     
 618         Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) {
 619             super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex);
 620         }
 621     
 622         protected void implReset() {
 623             currentState = SBCS;
 624         }
 625     
 626         protected CoderResult implFlush(ByteBuffer out) {
 627             if (currentState == DBCS) {
 628                 if (out.remaining() < 1)
 629                     return CoderResult.OVERFLOW;
 630                 out.put(SI);
 631             }
 632             implReset();
 633             return CoderResult.UNDERFLOW;
 634         }
 635     
 636         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
 637             char[] sa = src.array();
 638             int sp = src.arrayOffset() + src.position();
 639             int sl = src.arrayOffset() + src.limit();
 640             byte[] da = dst.array();
 641             int dp = dst.arrayOffset() + dst.position();
 642             int dl = dst.arrayOffset() + dst.limit();
 643     
 644             try {
 645                 while (sp < sl) {
 646                     char c = sa[sp];
 647                     int bb = encodeChar(c);
 648                     if (bb == UNMAPPABLE_ENCODING) {
 649                         if (Surrogate.is(c)) {
 650                             if (sgp().parse(c, sa, sp, sl) < 0)
 651                                 return sgp.error();
 652                             return sgp.unmappableResult();
 653                         }
 654                         return CoderResult.unmappableForLength(1);
 655                     }
 656                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
 657                         if (currentState == SBCS) {
 658                             if (dl - dp < 1)
 659                                 return CoderResult.OVERFLOW;
 660                             currentState = DBCS;
 661                             da[dp++] = SO;
 662                         }
 663                         if (dl - dp < 2)
 664                             return CoderResult.OVERFLOW;
 665                         da[dp++] = (byte)(bb >> 8);
 666                         da[dp++] = (byte)bb;
 667                     } else {                    // SingleByte
 668                         if (currentState == DBCS) {
 669                             if (dl - dp < 1)
 670                                 return CoderResult.OVERFLOW;
 671                             currentState = SBCS;
 672                             da[dp++] = SI;
 673                         }
 674                         if (dl - dp < 1)
 675                             return CoderResult.OVERFLOW;
 676                         da[dp++] = (byte)bb;
 677 
 678                     }
 679                     sp++;
 680                 }
 681                 return CoderResult.UNDERFLOW;
 682             } finally {
 683                 src.position(sp - src.arrayOffset());
 684                 dst.position(dp - dst.arrayOffset());
 685             }
 686         }
 687     
 688         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
 689             int mark = src.position();
 690             try {
 691                 while (src.hasRemaining()) {
 692                     char c = src.get();
 693                     int bb = encodeChar(c);
 694                     if (bb == UNMAPPABLE_ENCODING) {
 695                         if (Surrogate.is(c)) {
 696                             if (sgp().parse(c, src) < 0)
 697                                 return sgp.error();
 698                             return sgp.unmappableResult();
 699                         }
 700                         return CoderResult.unmappableForLength(1);
 701                     }
 702                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
 703                         if (currentState == SBCS) {
 704                             if (dst.remaining() < 1)
 705                                 return CoderResult.OVERFLOW;
 706                             currentState = DBCS;
 707                             dst.put(SO);
 708                         }
 709                         if (dst.remaining() < 2)
 710                             return CoderResult.OVERFLOW;
 711                         dst.put((byte)(bb >> 8));
 712                         dst.put((byte)(bb));
 713                     } else {                  // Single-byte
 714                         if (currentState == DBCS) {
 715                             if (dst.remaining() < 1)
 716                                 return CoderResult.OVERFLOW;
 717                             currentState = SBCS;
 718                             dst.put(SI);
 719                         } 
 720                         if (dst.remaining() < 1)
 721                             return CoderResult.OVERFLOW;
 722                         dst.put((byte)bb);
 723                     }
 724                     mark++;
 725                 }
 726                 return CoderResult.UNDERFLOW;
 727             } finally {
 728                 src.position(mark);
 729             }
 730         }
 731     }
 732 
 733     // EUC_SIMPLE
 734     public static class Encoder_EUC_SIM extends Encoder {
 735         Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) {
 736             super(cs, c2b, c2bIndex);
 737         }
 738     }
 739 }