1 /*
   2  * Copyright (c) 2008, 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;
  27 
  28 import java.nio.Buffer;
  29 import java.nio.ByteBuffer;
  30 import java.nio.CharBuffer;
  31 import java.nio.charset.Charset;
  32 import java.nio.charset.CharsetDecoder;
  33 import java.nio.charset.CharsetEncoder;
  34 import java.nio.charset.CoderResult;
  35 import java.util.Arrays;
  36 import static sun.nio.cs.CharsetMapping.*;
  37 
  38 public class SingleByte
  39 {
  40     private static final CoderResult withResult(CoderResult cr,
  41                                                 Buffer src, int sp,
  42                                                 Buffer dst, int dp)
  43     {
  44         src.position(sp - src.arrayOffset());
  45         dst.position(dp - dst.arrayOffset());
  46         return cr;
  47     }
  48 
  49     public static final class Decoder extends CharsetDecoder
  50                                       implements ArrayDecoder {
  51         private final char[] b2c;
  52         private final boolean isASCIICompatible;
  53 
  54         public Decoder(Charset cs, char[] b2c) {
  55             super(cs, 1.0f, 1.0f);
  56             this.b2c = b2c;
  57             this.isASCIICompatible = false;
  58         }
  59 
  60         public Decoder(Charset cs, char[] b2c, boolean isASCIICompatible) {
  61             super(cs, 1.0f, 1.0f);
  62             this.b2c = b2c;
  63             this.isASCIICompatible = isASCIICompatible;
  64         }
  65 
  66         private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
  67             byte[] sa = src.array();
  68             int sp = src.arrayOffset() + src.position();
  69             int sl = src.arrayOffset() + src.limit();
  70 
  71             char[] da = dst.array();
  72             int dp = dst.arrayOffset() + dst.position();
  73             int dl = dst.arrayOffset() + dst.limit();
  74 
  75             CoderResult cr = CoderResult.UNDERFLOW;
  76             if ((dl - dp) < (sl - sp)) {
  77                 sl = sp + (dl - dp);
  78                 cr = CoderResult.OVERFLOW;
  79             }
  80 
  81             while (sp < sl) {
  82                 char c = decode(sa[sp]);
  83                 if (c == UNMAPPABLE_DECODING) {
  84                     return withResult(CoderResult.unmappableForLength(1),
  85                                src, sp, dst, dp);
  86                 }
  87                 da[dp++] = c;
  88                 sp++;
  89             }
  90             return withResult(cr, src, sp, dst, dp);
  91         }
  92 
  93         private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
  94             int mark = src.position();
  95             try {
  96                 while (src.hasRemaining()) {
  97                     char c = decode(src.get());
  98                     if (c == UNMAPPABLE_DECODING)
  99                         return CoderResult.unmappableForLength(1);
 100                     if (!dst.hasRemaining())
 101                         return CoderResult.OVERFLOW;
 102                     dst.put(c);
 103                     mark++;
 104                 }
 105                 return CoderResult.UNDERFLOW;
 106             } finally {
 107                 src.position(mark);
 108             }
 109         }
 110 
 111         protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 112             if (src.hasArray() && dst.hasArray())
 113                 return decodeArrayLoop(src, dst);
 114             else
 115                 return decodeBufferLoop(src, dst);
 116         }
 117 
 118         public final char decode(int b) {
 119             return b2c[b + 128];
 120         }
 121 
 122         private char repl = '\uFFFD';
 123         protected void implReplaceWith(String newReplacement) {
 124             repl = newReplacement.charAt(0);
 125         }
 126 
 127         @Override
 128         public int decode(byte[] src, int sp, int len, char[] dst) {
 129             if (len > dst.length)
 130                 len = dst.length;
 131             int dp = 0;
 132             while (dp < len) {
 133                 dst[dp] = decode(src[sp++]);
 134                 if (dst[dp] == UNMAPPABLE_DECODING) {
 135                     dst[dp] = repl;
 136                 }
 137                 dp++;
 138             }
 139             return dp;
 140         }
 141 
 142         @Override
 143         public boolean isASCIICompatible() {
 144             return isASCIICompatible;
 145         }
 146     }
 147 
 148     public static final class Encoder extends CharsetEncoder
 149                                       implements ArrayEncoder {
 150         private Surrogate.Parser sgp;
 151         private final char[] c2b;
 152         private final char[] c2bIndex;
 153         private final boolean isASCIICompatible;
 154 
 155         public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) {
 156             super(cs, 1.0f, 1.0f);
 157             this.c2b = c2b;
 158             this.c2bIndex = c2bIndex;
 159             this.isASCIICompatible = isASCIICompatible;
 160         }
 161 
 162         public boolean canEncode(char c) {
 163             return encode(c) != UNMAPPABLE_ENCODING;
 164         }
 165 
 166         public boolean isLegalReplacement(byte[] repl) {
 167             return ((repl.length == 1 && repl[0] == (byte)'?') ||
 168                     super.isLegalReplacement(repl));
 169         }
 170 
 171         private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
 172             char[] sa = src.array();
 173             int sp = src.arrayOffset() + src.position();
 174             int sl = src.arrayOffset() + src.limit();
 175 
 176             byte[] da = dst.array();
 177             int dp = dst.arrayOffset() + dst.position();
 178             int dl = dst.arrayOffset() + dst.limit();
 179             int len  = Math.min(dl - dp, sl - sp);
 180 
 181             while (len-- > 0) {
 182                 char c = sa[sp];
 183                 int b = encode(c);
 184                 if (b == UNMAPPABLE_ENCODING) {
 185                     if (Character.isSurrogate(c)) {
 186                         if (sgp == null)
 187                             sgp = new Surrogate.Parser();
 188                         if (sgp.parse(c, sa, sp, sl) < 0) {
 189                             return withResult(sgp.error(), src, sp, dst, dp);
 190                         }
 191                         return withResult(sgp.unmappableResult(), src, sp, dst, dp);
 192                     }
 193                     return withResult(CoderResult.unmappableForLength(1),
 194                                src, sp, dst, dp);
 195                 }
 196                 da[dp++] = (byte)b;
 197                 sp++;
 198             }
 199             return withResult(sp < sl ? CoderResult.OVERFLOW : CoderResult.UNDERFLOW,
 200                               src, sp, dst, dp);
 201         }
 202 
 203         private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
 204             int mark = src.position();
 205             try {
 206                 while (src.hasRemaining()) {
 207                     char c = src.get();
 208                     int b = encode(c);
 209                     if (b == UNMAPPABLE_ENCODING) {
 210                         if (Character.isSurrogate(c)) {
 211                             if (sgp == null)
 212                                 sgp = new Surrogate.Parser();
 213                             if (sgp.parse(c, src) < 0)
 214                                 return sgp.error();
 215                             return sgp.unmappableResult();
 216                         }
 217                         return CoderResult.unmappableForLength(1);
 218                     }
 219                     if (!dst.hasRemaining())
 220                         return CoderResult.OVERFLOW;
 221                     dst.put((byte)b);
 222                     mark++;
 223                 }
 224                 return CoderResult.UNDERFLOW;
 225             } finally {
 226                 src.position(mark);
 227             }
 228         }
 229 
 230         protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
 231             if (src.hasArray() && dst.hasArray())
 232                 return encodeArrayLoop(src, dst);
 233             else
 234                 return encodeBufferLoop(src, dst);
 235         }
 236 
 237         public final int encode(char ch) {
 238             char index = c2bIndex[ch >> 8];
 239             if (index == UNMAPPABLE_ENCODING)
 240                 return UNMAPPABLE_ENCODING;
 241             return c2b[index + (ch & 0xff)];
 242         }
 243 
 244         private byte repl = (byte)'?';
 245         protected void implReplaceWith(byte[] newReplacement) {
 246             repl = newReplacement[0];
 247         }
 248 
 249         public int encode(char[] src, int sp, int len, byte[] dst) {
 250             int dp = 0;
 251             int sl = sp + Math.min(len, dst.length);
 252             while (sp < sl) {
 253                 char c = src[sp++];
 254                 int b = encode(c);
 255                 if (b != UNMAPPABLE_ENCODING) {
 256                     dst[dp++] = (byte)b;
 257                     continue;
 258                 }
 259                 if (Character.isHighSurrogate(c) && sp < sl &&
 260                     Character.isLowSurrogate(src[sp])) {
 261                     if (len > dst.length) {
 262                         sl++;
 263                         len--;
 264                     }
 265                     sp++;
 266                 }
 267                 dst[dp++] = repl;
 268             }
 269             return dp;
 270         }
 271 
 272         @Override
 273         public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
 274             int dp = 0;
 275             int sl = sp + Math.min(len, dst.length);
 276             while (sp < sl) {
 277                 char c = (char)(src[sp++] & 0xff);
 278                 int b = encode(c);
 279                 if (b == UNMAPPABLE_ENCODING) {
 280                     dst[dp++] = repl;
 281                 } else {
 282                     dst[dp++] = (byte)b;
 283                 }
 284             }
 285             return dp;
 286         }
 287 
 288         @Override
 289         public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
 290             int dp = 0;
 291             int sl = sp + Math.min(len, dst.length);
 292             while (sp < sl) {
 293                 char c = StringUTF16.getChar(src, sp++);
 294                 int b = encode(c);
 295                 if (b != UNMAPPABLE_ENCODING) {
 296                     dst[dp++] = (byte)b;
 297                     continue;
 298                 }
 299                 if (Character.isHighSurrogate(c) && sp < sl &&
 300                     Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
 301                     if (len > dst.length) {
 302                         sl++;
 303                         len--;
 304                     }
 305                     sp++;
 306                 }
 307                 dst[dp++] = repl;
 308             }
 309             return dp;
 310         }
 311 
 312         @Override
 313         public boolean isASCIICompatible() {
 314             return isASCIICompatible;
 315         }
 316     }
 317 
 318     // init the c2b and c2bIndex tables from b2c.
 319     public static void initC2B(char[] b2c, char[] c2bNR,
 320                                char[] c2b, char[] c2bIndex) {
 321         for (int i = 0; i < c2bIndex.length; i++)
 322             c2bIndex[i] = UNMAPPABLE_ENCODING;
 323         for (int i = 0; i < c2b.length; i++)
 324             c2b[i] = UNMAPPABLE_ENCODING;
 325         int off = 0;
 326         for (int i = 0; i < b2c.length; i++) {
 327             char c = b2c[i];
 328             if (c == UNMAPPABLE_DECODING)
 329                 continue;
 330             int index = (c >> 8);
 331             if (c2bIndex[index] == UNMAPPABLE_ENCODING) {
 332                 c2bIndex[index] = (char)off;
 333                 off += 0x100;
 334             }
 335             index = c2bIndex[index] + (c & 0xff);
 336             c2b[index] = (char)((i>=0x80)?(i-0x80):(i+0x80));
 337         }
 338         if (c2bNR != null) {
 339             // c-->b nr entries
 340             int i = 0;
 341             while (i < c2bNR.length) {
 342                 char b = c2bNR[i++];
 343                 char c = c2bNR[i++];
 344                 int index = (c >> 8);
 345                 if (c2bIndex[index] == UNMAPPABLE_ENCODING) {
 346                     c2bIndex[index] = (char)off;
 347                     off += 0x100;
 348                 }
 349                 index = c2bIndex[index] + (c & 0xff);
 350                 c2b[index] = b;
 351             }
 352         }
 353     }
 354 }