1 /*
   2  * Copyright (c) 2009, 2012, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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 sun.nio.cs.ArrayDecoder;
  37 import sun.nio.cs.ArrayEncoder;
  38 import static sun.nio.cs.CharsetMapping.*;
  39 
  40 /*
  41  * Four types of "DoubleByte" charsets are implemented in this class
  42  * (1)DoubleByte
  43  *    The "mostly widely used" multibyte charset, a combination of
  44  *    a singlebyte character set (usually the ASCII charset) and a
  45  *    doublebyte character set. The codepoint values of singlebyte
  46  *    and doublebyte don't overlap. Microsoft's multibyte charsets
  47  *    and IBM's "DBCS_ASCII" charsets, such as IBM1381, 942, 943,
  48  *    948, 949 and 950 are such charsets.
  49  *
  50  * (2)DoubleByte_EBCDIC
  51  *    IBM EBCDIC Mix multibyte charset. Use SO and SI to shift (switch)
  52  *    in and out between the singlebyte character set and doublebyte
  53  *    character set.
  54  *
  55  * (3)DoubleByte_SIMPLE_EUC
  56  *    It's a "simple" form of EUC encoding scheme, only have the
  57  *    singlebyte character set G0 and one doublebyte character set
  58  *    G1 are defined, G2 (with SS2) and G3 (with SS3) are not used.
  59  *    So it is actually the same as the "typical" type (1) mentioned
  60  *    above, except it return "malformed" for the SS2 and SS3 when
  61  *    decoding.
  62  *
  63  * (4)DoubleByte ONLY
  64  *    A "pure" doublebyte only character set. From implementation
  65  *    point of view, this is the type (1) with "decodeSingle" always
  66  *    returns unmappable.
  67  *
  68  * For simplicity, all implementations share the same decoding and
  69  * encoding data structure.
  70  *
  71  * Decoding:
  72  *
  73  *    char[][] b2c;
  74  *    char[] b2cSB;
  75  *    int b2Min, b2Max
  76  *
  77  *    public char decodeSingle(int b) {
  78  *        return b2cSB.[b];
  79  *    }
  80  *
  81  *    public char decodeDouble(int b1, int b2) {
  82  *        if (b2 < b2Min || b2 > b2Max)
  83  *            return UNMAPPABLE_DECODING;
  84  *         return b2c[b1][b2 - b2Min];
  85  *    }
  86  *
  87  *    (1)b2Min, b2Max are the corresponding min and max value of the
  88  *       low-half of the double-byte.
  89  *    (2)The high 8-bit/b1 of the double-byte are used to indexed into
  90  *       b2c array.
  91  *
  92  * Encoding:
  93  *
  94  *    char[] c2b;
  95  *    char[] c2bIndex;
  96  *
  97  *    public int encodeChar(char ch) {
  98  *        return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
  99  *    }
 100  *
 101  */
 102 
 103 public class DoubleByte {
 104 
 105     public final static char[] B2C_UNMAPPABLE;
 106     static {
 107         B2C_UNMAPPABLE = new char[0x100];
 108         Arrays.fill(B2C_UNMAPPABLE, UNMAPPABLE_DECODING);
 109     }
 110 
 111     public static class Decoder extends CharsetDecoder
 112                                 implements DelegatableDecoder, ArrayDecoder
 113     {
 114 
 115         final char[][] b2c;
 116         final char[] b2cSB;
 117         final int b2Min;
 118         final int b2Max;
 119 
 120         // for SimpleEUC override
 121         protected CoderResult crMalformedOrUnderFlow(int b) {
 122             return CoderResult.UNDERFLOW;
 123         }
 124 
 125         protected CoderResult crMalformedOrUnmappable(int b) {
 126             return CoderResult.unmappableForLength(2);
 127         }
 128 
 129         Decoder(Charset cs, float avgcpb, float maxcpb,
 130                 char[][] b2c, char[] b2cSB,
 131                 int b2Min, int b2Max) {
 132             super(cs, avgcpb, maxcpb);
 133             this.b2c = b2c;
 134             this.b2cSB = b2cSB;
 135             this.b2Min = b2Min;
 136             this.b2Max = b2Max;
 137         }
 138 
 139         Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
 140             this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max);
 141         }
 142 
 143         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
 144             byte[] sa = src.array();
 145             int sp = src.arrayOffset() + src.position();
 146             int sl = src.arrayOffset() + src.limit();
 147 
 148             char[] da = dst.array();
 149             int dp = dst.arrayOffset() + dst.position();
 150             int dl = dst.arrayOffset() + dst.limit();
 151 
 152             try {
 153                 while (sp < sl && dp < dl) {
 154                     // inline the decodeSingle/Double() for better performance
 155                     int inSize = 1;
 156                     int b1 = sa[sp] & 0xff;
 157                     char c = b2cSB[b1];
 158                     if (c == UNMAPPABLE_DECODING) {
 159                         if (sl - sp < 2)
 160                             return crMalformedOrUnderFlow(b1);
 161                         int b2 = sa[sp + 1] & 0xff;
 162                         if (b2 < b2Min || b2 > b2Max ||
 163                             (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 164                             return crMalformedOrUnmappable(b1);
 165                         }
 166                         inSize++;
 167                     }
 168                     da[dp++] = c;
 169                     sp += inSize;
 170                 }
 171                 return (sp >= sl) ? CoderResult.UNDERFLOW
 172                                   : CoderResult.OVERFLOW;
 173             } finally {
 174                 src.position(sp - src.arrayOffset());
 175                 dst.position(dp - dst.arrayOffset());
 176             }
 177         }
 178 
 179         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
 180             int mark = src.position();
 181             try {
 182 
 183                 while (src.hasRemaining() && dst.hasRemaining()) {
 184                     int b1 = src.get() & 0xff;
 185                     char c = b2cSB[b1];
 186                     int inSize = 1;
 187                     if (c == UNMAPPABLE_DECODING) {
 188                         if (src.remaining() < 1)
 189                             return crMalformedOrUnderFlow(b1);
 190                         int b2 = src.get() & 0xff;
 191                         if (b2 < b2Min || b2 > b2Max ||
 192                             (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING)
 193                             return crMalformedOrUnmappable(b1);
 194                         inSize++;
 195                     }
 196                     dst.put(c);
 197                     mark += inSize;
 198                 }
 199                 return src.hasRemaining()? CoderResult.OVERFLOW
 200                                          : CoderResult.UNDERFLOW;
 201             } finally {
 202                 src.position(mark);
 203             }
 204         }
 205 
 206         // Make some protected methods public for use by JISAutoDetect
 207         public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 208             if (src.hasArray() && dst.hasArray())
 209                 return decodeArrayLoop(src, dst);
 210             else
 211                 return decodeBufferLoop(src, dst);
 212         }
 213 
 214         public int decode(byte[] src, int sp, int len, char[] dst) {
 215             int dp = 0;
 216             int sl = sp + len;
 217             char repl = replacement().charAt(0);
 218             while (sp < sl) {
 219                 int b1 = src[sp++] & 0xff;
 220                 char c = b2cSB[b1];
 221                 if (c == UNMAPPABLE_DECODING) {
 222                     if (sp < sl) {
 223                         int b2 = src[sp++] & 0xff;
 224                         if (b2 >= b2Min && b2 <= b2Max) {
 225                             c = b2c[b1][b2 - b2Min];
 226                         }
 227                     }
 228                     if (c == UNMAPPABLE_DECODING) {
 229                         c = repl;
 230                     }
 231                 }
 232                 dst[dp++] = c;
 233             }
 234             return dp;
 235         }
 236 
 237         public void implReset() {
 238             super.implReset();
 239         }
 240 
 241         public CoderResult implFlush(CharBuffer out) {
 242             return super.implFlush(out);
 243         }
 244 
 245         // decode loops are not using decodeSingle/Double() for performance
 246         // reason.
 247         public char decodeSingle(int b) {
 248             return b2cSB[b];
 249         }
 250 
 251         public char decodeDouble(int b1, int b2) {
 252             if (b1 < 0 || b1 > b2c.length ||
 253                 b2 < b2Min || b2 > b2Max)
 254                 return UNMAPPABLE_DECODING;
 255             return  b2c[b1][b2 - b2Min];
 256         }
 257     }
 258 
 259     // IBM_EBCDIC_DBCS
 260     public static class Decoder_EBCDIC extends Decoder {
 261         private static final int SBCS = 0;
 262         private static final int DBCS = 1;
 263         private static final int SO = 0x0e;
 264         private static final int SI = 0x0f;
 265         private int  currentState;
 266 
 267         Decoder_EBCDIC(Charset cs,
 268                        char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
 269             super(cs, b2c, b2cSB, b2Min, b2Max);
 270         }
 271 
 272         public void implReset() {
 273             currentState = SBCS;
 274         }
 275 
 276         // Check validity of dbcs ebcdic byte pair values
 277         //
 278         // First byte : 0x41 -- 0xFE
 279         // Second byte: 0x41 -- 0xFE
 280         // Doublebyte blank: 0x4040
 281         //
 282         // The validation implementation in "old" DBCS_IBM_EBCDIC and sun.io
 283         // as
 284         //            if ((b1 != 0x40 || b2 != 0x40) &&
 285         //                (b2 < 0x41 || b2 > 0xfe)) {...}
 286         // is not correct/complete (range check for b1)
 287         //
 288         private static boolean isDoubleByte(int b1, int b2) {
 289             return (0x41 <= b1 && b1 <= 0xfe && 0x41 <= b2 && b2 <= 0xfe)
 290                    || (b1 == 0x40 && b2 == 0x40); // DBCS-HOST SPACE
 291         }
 292 
 293         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
 294             byte[] sa = src.array();
 295             int sp = src.arrayOffset() + src.position();
 296             int sl = src.arrayOffset() + src.limit();
 297             char[] da = dst.array();
 298             int dp = dst.arrayOffset() + dst.position();
 299             int dl = dst.arrayOffset() + dst.limit();
 300 
 301             try {
 302                 // don't check dp/dl together here, it's possible to
 303                 // decdoe a SO/SI without space in output buffer.
 304                 while (sp < sl) {
 305                     int b1 = sa[sp] & 0xff;
 306                     int inSize = 1;
 307                     if (b1 == SO) {  // Shift out
 308                         if (currentState != SBCS)
 309                             return CoderResult.malformedForLength(1);
 310                         else
 311                             currentState = DBCS;
 312                     } else if (b1 == SI) {
 313                         if (currentState != DBCS)
 314                             return CoderResult.malformedForLength(1);
 315                         else
 316                             currentState = SBCS;
 317                     } else {
 318                         char c =  UNMAPPABLE_DECODING;
 319                         if (currentState == SBCS) {
 320                             c = b2cSB[b1];
 321                             if (c == UNMAPPABLE_DECODING)
 322                                 return CoderResult.unmappableForLength(1);
 323                         } else {
 324                             if (sl - sp < 2)
 325                                 return CoderResult.UNDERFLOW;
 326                             int b2 = sa[sp + 1] & 0xff;
 327                             if (b2 < b2Min || b2 > b2Max ||
 328                                 (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 329                                 if (!isDoubleByte(b1, b2))
 330                                     return CoderResult.malformedForLength(2);
 331                                 return CoderResult.unmappableForLength(2);
 332                             }
 333                             inSize++;
 334                         }
 335                         if (dl - dp < 1)
 336                             return CoderResult.OVERFLOW;
 337 
 338                         da[dp++] = c;
 339                     }
 340                     sp += inSize;
 341                 }
 342                 return CoderResult.UNDERFLOW;
 343             } finally {
 344                 src.position(sp - src.arrayOffset());
 345                 dst.position(dp - dst.arrayOffset());
 346             }
 347         }
 348 
 349         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
 350             int mark = src.position();
 351             try {
 352                 while (src.hasRemaining()) {
 353                     int b1 = src.get() & 0xff;
 354                     int inSize = 1;
 355                     if (b1 == SO) {  // Shift out
 356                         if (currentState != SBCS)
 357                             return CoderResult.malformedForLength(1);
 358                         else
 359                             currentState = DBCS;
 360                     } else if (b1 == SI) {
 361                         if (currentState != DBCS)
 362                             return CoderResult.malformedForLength(1);
 363                         else
 364                             currentState = SBCS;
 365                     } else {
 366                         char c = UNMAPPABLE_DECODING;
 367                         if (currentState == SBCS) {
 368                             c = b2cSB[b1];
 369                             if (c == UNMAPPABLE_DECODING)
 370                                 return CoderResult.unmappableForLength(1);
 371                         } else {
 372                             if (src.remaining() < 1)
 373                                 return CoderResult.UNDERFLOW;
 374                             int b2 = src.get()&0xff;
 375                             if (b2 < b2Min || b2 > b2Max ||
 376                                 (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 377                                 if (!isDoubleByte(b1, b2))
 378                                     return CoderResult.malformedForLength(2);
 379                                 return CoderResult.unmappableForLength(2);
 380                             }
 381                             inSize++;
 382                         }
 383 
 384                         if (dst.remaining() < 1)
 385                             return CoderResult.OVERFLOW;
 386 
 387                         dst.put(c);
 388                     }
 389                     mark += inSize;
 390                 }
 391                 return CoderResult.UNDERFLOW;
 392             } finally {
 393                 src.position(mark);
 394             }
 395         }
 396 
 397         public int decode(byte[] src, int sp, int len, char[] dst) {
 398             int dp = 0;
 399             int sl = sp + len;
 400             currentState = SBCS;
 401             char repl = replacement().charAt(0);
 402             while (sp < sl) {
 403                 int b1 = src[sp++] & 0xff;
 404                 if (b1 == SO) {  // Shift out
 405                     if (currentState != SBCS)
 406                         dst[dp++] = repl;
 407                     else
 408                         currentState = DBCS;
 409                 } else if (b1 == SI) {
 410                     if (currentState != DBCS)
 411                         dst[dp++] = repl;
 412                     else
 413                         currentState = SBCS;
 414                 } else {
 415                     char c =  UNMAPPABLE_DECODING;
 416                     if (currentState == SBCS) {
 417                         c = b2cSB[b1];
 418                         if (c == UNMAPPABLE_DECODING)
 419                             c = repl;
 420                     } else {
 421                         if (sl == sp) {
 422                             c = repl;
 423                         } else {
 424                             int b2 = src[sp++] & 0xff;
 425                             if (b2 < b2Min || b2 > b2Max ||
 426                                 (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 427                                 c = repl;
 428                             }
 429                         }
 430                     }
 431                     dst[dp++] = c;
 432                 }
 433             }
 434             return dp;
 435         }
 436     }
 437 
 438     // DBCS_ONLY
 439     public static class Decoder_DBCSONLY extends Decoder {
 440         static final char[] b2cSB_UNMAPPABLE;
 441         static {
 442             b2cSB_UNMAPPABLE = new char[0x100];
 443             Arrays.fill(b2cSB_UNMAPPABLE, UNMAPPABLE_DECODING);
 444         }
 445         Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
 446             super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max);
 447         }
 448     }
 449 
 450     // EUC_SIMPLE
 451     // The only thing we need to "override" is to check SS2/SS3 and
 452     // return "malformed" if found
 453     public static class Decoder_EUC_SIM extends Decoder {
 454         private final int SS2 =  0x8E;
 455         private final int SS3 =  0x8F;
 456 
 457         Decoder_EUC_SIM(Charset cs,
 458                         char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
 459             super(cs, b2c, b2cSB, b2Min, b2Max);
 460         }
 461 
 462         // No support provided for G2/G3 for SimpleEUC
 463         protected CoderResult crMalformedOrUnderFlow(int b) {
 464             if (b == SS2 || b == SS3 )
 465                 return CoderResult.malformedForLength(1);
 466             return CoderResult.UNDERFLOW;
 467         }
 468 
 469         protected CoderResult crMalformedOrUnmappable(int b) {
 470             if (b == SS2 || b == SS3 )
 471                 return CoderResult.malformedForLength(1);
 472             return CoderResult.unmappableForLength(2);
 473         }
 474 
 475         public int decode(byte[] src, int sp, int len, char[] dst) {
 476             int dp = 0;
 477             int sl = sp + len;
 478             char repl = replacement().charAt(0);
 479             while (sp < sl) {
 480                 int b1 = src[sp++] & 0xff;
 481                 char c = b2cSB[b1];
 482                 if (c == UNMAPPABLE_DECODING) {
 483                     if (sp < sl) {
 484                         int b2 = src[sp++] & 0xff;
 485                         if (b2 < b2Min || b2 > b2Max ||
 486                             (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
 487                             if (b1 == SS2 || b1 == SS3) {
 488                                 sp--;
 489                             }
 490                             c = repl;
 491                         }
 492                     } else {
 493                         c = repl;
 494                     }
 495                 }
 496                 dst[dp++] = c;
 497             }
 498             return dp;
 499         }
 500     }
 501 
 502     public static class Encoder extends CharsetEncoder
 503                                 implements ArrayEncoder
 504     {
 505         final int MAX_SINGLEBYTE = 0xff;
 506         private final char[] c2b;
 507         private final char[] c2bIndex;
 508         Surrogate.Parser sgp;
 509 
 510         protected Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
 511             super(cs, 2.0f, 2.0f);
 512             this.c2b = c2b;
 513             this.c2bIndex = c2bIndex;
 514         }
 515 
 516         Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) {
 517             super(cs, avg, max, repl);
 518             this.c2b = c2b;
 519             this.c2bIndex = c2bIndex;
 520         }
 521 
 522         public boolean canEncode(char c) {
 523             return encodeChar(c) != UNMAPPABLE_ENCODING;
 524         }
 525 
 526         Surrogate.Parser sgp() {
 527             if (sgp == null)
 528                 sgp = new Surrogate.Parser();
 529             return sgp;
 530         }
 531 
 532         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
 533             char[] sa = src.array();
 534             int sp = src.arrayOffset() + src.position();
 535             int sl = src.arrayOffset() + src.limit();
 536 
 537             byte[] da = dst.array();
 538             int dp = dst.arrayOffset() + dst.position();
 539             int dl = dst.arrayOffset() + dst.limit();
 540 
 541             try {
 542                 while (sp < sl) {
 543                     char c = sa[sp];
 544                     int bb = encodeChar(c);
 545                     if (bb == UNMAPPABLE_ENCODING) {
 546                         if (Character.isSurrogate(c)) {
 547                             if (sgp().parse(c, sa, sp, sl) < 0)
 548                                 return sgp.error();
 549                             return sgp.unmappableResult();
 550                         }
 551                         return CoderResult.unmappableForLength(1);
 552                     }
 553 
 554                     if (bb > MAX_SINGLEBYTE) {    // DoubleByte
 555                         if (dl - dp < 2)
 556                             return CoderResult.OVERFLOW;
 557                         da[dp++] = (byte)(bb >> 8);
 558                         da[dp++] = (byte)bb;
 559                     } else {                      // SingleByte
 560                         if (dl - dp < 1)
 561                             return CoderResult.OVERFLOW;
 562                         da[dp++] = (byte)bb;
 563                     }
 564 
 565                     sp++;
 566                 }
 567                 return CoderResult.UNDERFLOW;
 568             } finally {
 569                 src.position(sp - src.arrayOffset());
 570                 dst.position(dp - dst.arrayOffset());
 571             }
 572         }
 573 
 574         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
 575             int mark = src.position();
 576             try {
 577                 while (src.hasRemaining()) {
 578                     char c = src.get();
 579                     int bb = encodeChar(c);
 580                     if (bb == UNMAPPABLE_ENCODING) {
 581                         if (Character.isSurrogate(c)) {
 582                             if (sgp().parse(c, src) < 0)
 583                                 return sgp.error();
 584                             return sgp.unmappableResult();
 585                         }
 586                         return CoderResult.unmappableForLength(1);
 587                     }
 588                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
 589                         if (dst.remaining() < 2)
 590                             return CoderResult.OVERFLOW;
 591                         dst.put((byte)(bb >> 8));
 592                         dst.put((byte)(bb));
 593                     } else {
 594                         if (dst.remaining() < 1)
 595                         return CoderResult.OVERFLOW;
 596                         dst.put((byte)bb);
 597                     }
 598                     mark++;
 599                 }
 600                 return CoderResult.UNDERFLOW;
 601             } finally {
 602                 src.position(mark);
 603             }
 604         }
 605 
 606         protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
 607             if (src.hasArray() && dst.hasArray())
 608                 return encodeArrayLoop(src, dst);
 609             else
 610                 return encodeBufferLoop(src, dst);
 611         }
 612 
 613         protected byte[] repl = replacement();
 614         protected void implReplaceWith(byte[] newReplacement) {
 615             repl = newReplacement;
 616         }
 617 
 618         public int encode(char[] src, int sp, int len, byte[] dst) {
 619             int dp = 0;
 620             int sl = sp + len;
 621             int dl = dst.length;
 622             while (sp < sl) {
 623                 char c = src[sp++];
 624                 int bb = encodeChar(c);
 625                 if (bb == UNMAPPABLE_ENCODING) {
 626                     if (Character.isHighSurrogate(c) && sp < sl &&
 627                         Character.isLowSurrogate(src[sp])) {
 628                         sp++;
 629                     }
 630                     dst[dp++] = repl[0];
 631                     if (repl.length > 1)
 632                         dst[dp++] = repl[1];
 633                     continue;
 634                 } //else
 635                 if (bb > MAX_SINGLEBYTE) { // DoubleByte
 636                     dst[dp++] = (byte)(bb >> 8);
 637                     dst[dp++] = (byte)bb;
 638                 } else {                          // SingleByte
 639                     dst[dp++] = (byte)bb;
 640                 }
 641 
 642             }
 643             return dp;
 644         }
 645 
 646         public int encodeChar(char ch) {
 647             return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
 648         }
 649 
 650         // init the c2b and c2bIndex tables from b2c.
 651         static void initC2B(String[] b2c, String b2cSB, String b2cNR,  String c2bNR,
 652                             int b2Min, int b2Max,
 653                             char[] c2b, char[] c2bIndex)
 654         {
 655             Arrays.fill(c2b, (char)UNMAPPABLE_ENCODING);
 656             int off = 0x100;
 657 
 658             char[][] b2c_ca = new char[b2c.length][];
 659             char[] b2cSB_ca = null;
 660             if (b2cSB != null)
 661                 b2cSB_ca = b2cSB.toCharArray();
 662 
 663             for (int i = 0; i < b2c.length; i++) {
 664                 if (b2c[i] == null)
 665                     continue;
 666                 b2c_ca[i] = b2c[i].toCharArray();
 667             }
 668 
 669             if (b2cNR != null) {
 670                 int j = 0;
 671                 while (j < b2cNR.length()) {
 672                     char b  = b2cNR.charAt(j++);
 673                     char c  = b2cNR.charAt(j++);
 674                     if (b < 0x100 && b2cSB_ca != null) {
 675                         if (b2cSB_ca[b] == c)
 676                             b2cSB_ca[b] = UNMAPPABLE_DECODING;
 677                     } else {
 678                         if (b2c_ca[b >> 8][(b & 0xff) - b2Min] == c)
 679                             b2c_ca[b >> 8][(b & 0xff) - b2Min] = UNMAPPABLE_DECODING;
 680                     }
 681                 }
 682             }
 683 
 684             if (b2cSB_ca != null) {      // SingleByte
 685                 for (int b = 0; b < b2cSB_ca.length; b++) {
 686                     char c = b2cSB_ca[b];
 687                     if (c == UNMAPPABLE_DECODING)
 688                         continue;
 689                     int index = c2bIndex[c >> 8];
 690                     if (index == 0) {
 691                         index = off;
 692                         off += 0x100;
 693                         c2bIndex[c >> 8] = (char)index;
 694                     }
 695                     c2b[index + (c & 0xff)] = (char)b;
 696                 }
 697             }
 698 
 699             for (int b1 = 0; b1 < b2c.length; b1++) {  // DoubleByte
 700                 char[] db = b2c_ca[b1];
 701                 if (db == null)
 702                     continue;
 703                 for (int b2 = b2Min; b2 <= b2Max; b2++) {
 704                     char c = db[b2 - b2Min];
 705                     if (c == UNMAPPABLE_DECODING)
 706                         continue;
 707                     int index = c2bIndex[c >> 8];
 708                     if (index == 0) {
 709                         index = off;
 710                         off += 0x100;
 711                         c2bIndex[c >> 8] = (char)index;
 712                     }
 713                     c2b[index + (c & 0xff)] = (char)((b1 << 8) | b2);
 714                 }
 715             }
 716 
 717             if (c2bNR != null) {
 718                 // add c->b only nr entries
 719                 for (int i = 0; i < c2bNR.length(); i += 2) {
 720                     char b = c2bNR.charAt(i);
 721                     char c = c2bNR.charAt(i + 1);
 722                     int index = (c >> 8);
 723                     if (c2bIndex[index] == 0) {
 724                         c2bIndex[index] = (char)off;
 725                         off += 0x100;
 726                     }
 727                     index = c2bIndex[index] + (c & 0xff);
 728                     c2b[index] = b;
 729                 }
 730             }
 731         }
 732     }
 733 
 734     public static class Encoder_DBCSONLY extends Encoder {
 735         Encoder_DBCSONLY(Charset cs, byte[] repl,
 736                          char[] c2b, char[] c2bIndex) {
 737             super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex);
 738         }
 739 
 740         public int encodeChar(char ch) {
 741             int bb = super.encodeChar(ch);
 742             if (bb <= MAX_SINGLEBYTE)
 743                 return UNMAPPABLE_ENCODING;
 744             return bb;
 745         }
 746     }
 747 
 748 
 749 
 750     public static class Encoder_EBCDIC extends Encoder {
 751         static final int SBCS = 0;
 752         static final int DBCS = 1;
 753         static final byte SO = 0x0e;
 754         static final byte SI = 0x0f;
 755 
 756         protected int  currentState = SBCS;
 757 
 758         Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) {
 759             super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex);
 760         }
 761 
 762         protected void implReset() {
 763             currentState = SBCS;
 764         }
 765 
 766         protected CoderResult implFlush(ByteBuffer out) {
 767             if (currentState == DBCS) {
 768                 if (out.remaining() < 1)
 769                     return CoderResult.OVERFLOW;
 770                 out.put(SI);
 771             }
 772             implReset();
 773             return CoderResult.UNDERFLOW;
 774         }
 775 
 776         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
 777             char[] sa = src.array();
 778             int sp = src.arrayOffset() + src.position();
 779             int sl = src.arrayOffset() + src.limit();
 780             byte[] da = dst.array();
 781             int dp = dst.arrayOffset() + dst.position();
 782             int dl = dst.arrayOffset() + dst.limit();
 783 
 784             try {
 785                 while (sp < sl) {
 786                     char c = sa[sp];
 787                     int bb = encodeChar(c);
 788                     if (bb == UNMAPPABLE_ENCODING) {
 789                         if (Character.isSurrogate(c)) {
 790                             if (sgp().parse(c, sa, sp, sl) < 0)
 791                                 return sgp.error();
 792                             return sgp.unmappableResult();
 793                         }
 794                         return CoderResult.unmappableForLength(1);
 795                     }
 796                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
 797                         if (currentState == SBCS) {
 798                             if (dl - dp < 1)
 799                                 return CoderResult.OVERFLOW;
 800                             currentState = DBCS;
 801                             da[dp++] = SO;
 802                         }
 803                         if (dl - dp < 2)
 804                             return CoderResult.OVERFLOW;
 805                         da[dp++] = (byte)(bb >> 8);
 806                         da[dp++] = (byte)bb;
 807                     } else {                    // SingleByte
 808                         if (currentState == DBCS) {
 809                             if (dl - dp < 1)
 810                                 return CoderResult.OVERFLOW;
 811                             currentState = SBCS;
 812                             da[dp++] = SI;
 813                         }
 814                         if (dl - dp < 1)
 815                             return CoderResult.OVERFLOW;
 816                         da[dp++] = (byte)bb;
 817 
 818                     }
 819                     sp++;
 820                 }
 821                 return CoderResult.UNDERFLOW;
 822             } finally {
 823                 src.position(sp - src.arrayOffset());
 824                 dst.position(dp - dst.arrayOffset());
 825             }
 826         }
 827 
 828         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
 829             int mark = src.position();
 830             try {
 831                 while (src.hasRemaining()) {
 832                     char c = src.get();
 833                     int bb = encodeChar(c);
 834                     if (bb == UNMAPPABLE_ENCODING) {
 835                         if (Character.isSurrogate(c)) {
 836                             if (sgp().parse(c, src) < 0)
 837                                 return sgp.error();
 838                             return sgp.unmappableResult();
 839                         }
 840                         return CoderResult.unmappableForLength(1);
 841                     }
 842                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
 843                         if (currentState == SBCS) {
 844                             if (dst.remaining() < 1)
 845                                 return CoderResult.OVERFLOW;
 846                             currentState = DBCS;
 847                             dst.put(SO);
 848                         }
 849                         if (dst.remaining() < 2)
 850                             return CoderResult.OVERFLOW;
 851                         dst.put((byte)(bb >> 8));
 852                         dst.put((byte)(bb));
 853                     } else {                  // Single-byte
 854                         if (currentState == DBCS) {
 855                             if (dst.remaining() < 1)
 856                                 return CoderResult.OVERFLOW;
 857                             currentState = SBCS;
 858                             dst.put(SI);
 859                         }
 860                         if (dst.remaining() < 1)
 861                             return CoderResult.OVERFLOW;
 862                         dst.put((byte)bb);
 863                     }
 864                     mark++;
 865                 }
 866                 return CoderResult.UNDERFLOW;
 867             } finally {
 868                 src.position(mark);
 869             }
 870         }
 871 
 872         public int encode(char[] src, int sp, int len, byte[] dst) {
 873             int dp = 0;
 874             int sl = sp + len;
 875             while (sp < sl) {
 876                 char c = src[sp++];
 877                 int bb = encodeChar(c);
 878 
 879                 if (bb == UNMAPPABLE_ENCODING) {
 880                     if (Character.isHighSurrogate(c) && sp < sl &&
 881                         Character.isLowSurrogate(src[sp])) {
 882                         sp++;
 883                     }
 884                     dst[dp++] = repl[0];
 885                     if (repl.length > 1)
 886                         dst[dp++] = repl[1];
 887                     continue;
 888                 } //else
 889                 if (bb > MAX_SINGLEBYTE) {           // DoubleByte
 890                     if (currentState == SBCS) {
 891                         currentState = DBCS;
 892                         dst[dp++] = SO;
 893                     }
 894                     dst[dp++] = (byte)(bb >> 8);
 895                     dst[dp++] = (byte)bb;
 896                 } else {                             // SingleByte
 897                     if (currentState == DBCS) {
 898                          currentState = SBCS;
 899                          dst[dp++] = SI;
 900                     }
 901                     dst[dp++] = (byte)bb;
 902                 }
 903             }
 904 
 905             if (currentState == DBCS) {
 906                  currentState = SBCS;
 907                  dst[dp++] = SI;
 908             }
 909             return dp;
 910         }
 911     }
 912 
 913     // EUC_SIMPLE
 914     public static class Encoder_EUC_SIM extends Encoder {
 915         Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) {
 916             super(cs, c2b, c2bIndex);
 917         }
 918     }
 919 
 920 }