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