1 /*
   2  * Copyright (c) 2008, 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     final public static class Decoder extends CharsetDecoder
  50                                       implements ArrayDecoder {
  51         private final char[] b2c;
  52 
  53         public Decoder(Charset cs, char[] b2c) {
  54             super(cs, 1.0f, 1.0f);
  55             this.b2c = b2c;
  56         }
  57 
  58         private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
  59             byte[] sa = src.array();
  60             int sp = src.arrayOffset() + src.position();
  61             int sl = src.arrayOffset() + src.limit();
  62 
  63             char[] da = dst.array();
  64             int dp = dst.arrayOffset() + dst.position();
  65             int dl = dst.arrayOffset() + dst.limit();
  66 
  67             CoderResult cr = CoderResult.UNDERFLOW;
  68             if ((dl - dp) < (sl - sp)) {
  69                 sl = sp + (dl - dp);
  70                 cr = CoderResult.OVERFLOW;
  71             }
  72 
  73             while (sp < sl) {
  74                 char c = decode(sa[sp]);
  75                 if (c == UNMAPPABLE_DECODING) {
  76                     return withResult(CoderResult.unmappableForLength(1),
  77                                src, sp, dst, dp);
  78                 }
  79                 da[dp++] = c;
  80                 sp++;
  81             }
  82             return withResult(cr, src, sp, dst, dp);
  83         }
  84 
  85         private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
  86             int mark = src.position();
  87             try {
  88                 while (src.hasRemaining()) {
  89                     char c = decode(src.get());
  90                     if (c == UNMAPPABLE_DECODING)
  91                         return CoderResult.unmappableForLength(1);
  92                     if (!dst.hasRemaining())
  93                         return CoderResult.OVERFLOW;
  94                     dst.put(c);
  95                     mark++;
  96                 }
  97                 return CoderResult.UNDERFLOW;
  98             } finally {
  99                 src.position(mark);
 100             }
 101         }
 102 
 103         protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 104             if (src.hasArray() && dst.hasArray())
 105                 return decodeArrayLoop(src, dst);
 106             else
 107                 return decodeBufferLoop(src, dst);
 108         }
 109 
 110         public final char decode(int b) {
 111             return b2c[b + 128];
 112         }
 113 
 114         private char repl = '\uFFFD';
 115         protected void implReplaceWith(String newReplacement) {
 116             repl = newReplacement.charAt(0);
 117         }
 118 
 119         public int decode(byte[] src, int sp, int len, char[] dst) {
 120             if (len > dst.length)
 121                 len = dst.length;
 122             int dp = 0;
 123             while (dp < len) {
 124                 dst[dp] = decode(src[sp++]);
 125                 if (dst[dp] == UNMAPPABLE_DECODING) {
 126                     dst[dp] = repl;
 127                 }
 128                 dp++;
 129             }
 130             return dp;
 131         }
 132     }
 133 
 134     final public static class Encoder extends CharsetEncoder
 135                                       implements ArrayEncoder {
 136         private Surrogate.Parser sgp;
 137         private final char[] c2b;
 138         private final char[] c2bIndex;
 139 
 140         public Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
 141             super(cs, 1.0f, 1.0f);
 142             this.c2b = c2b;
 143             this.c2bIndex = c2bIndex;
 144         }
 145 
 146         public boolean canEncode(char c) {
 147             return encode(c) != UNMAPPABLE_ENCODING;
 148         }
 149 
 150         public boolean isLegalReplacement(byte[] repl) {
 151             return ((repl.length == 1 && repl[0] == (byte)'?') ||
 152                     super.isLegalReplacement(repl));
 153         }
 154 
 155         private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
 156             char[] sa = src.array();
 157             int sp = src.arrayOffset() + src.position();
 158             int sl = src.arrayOffset() + src.limit();
 159 
 160             byte[] da = dst.array();
 161             int dp = dst.arrayOffset() + dst.position();
 162             int dl = dst.arrayOffset() + dst.limit();
 163 
 164             CoderResult cr = CoderResult.UNDERFLOW;
 165             if ((dl - dp) < (sl - sp)) {
 166                 sl = sp + (dl - dp);
 167                 cr = CoderResult.OVERFLOW;
 168             }
 169 
 170             while (sp < sl) {
 171                 char c = sa[sp];
 172                 int b = encode(c);
 173                 if (b == UNMAPPABLE_ENCODING) {
 174                     if (Character.isSurrogate(c)) {
 175                         if (sgp == null)
 176                             sgp = new Surrogate.Parser();
 177                         if (sgp.parse(c, sa, sp, sl) < 0)
 178                             return withResult(sgp.error(), src, sp, dst, dp);
 179                         return withResult(sgp.unmappableResult(), src, sp, dst, dp);
 180                     }
 181                     return withResult(CoderResult.unmappableForLength(1),
 182                                src, sp, dst, dp);
 183                 }
 184                 da[dp++] = (byte)b;
 185                 sp++;
 186             }
 187             return withResult(cr, src, sp, dst, dp);
 188         }
 189 
 190         private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
 191             int mark = src.position();
 192             try {
 193                 while (src.hasRemaining()) {
 194                     char c = src.get();
 195                     int b = encode(c);
 196                     if (b == UNMAPPABLE_ENCODING) {
 197                         if (Character.isSurrogate(c)) {
 198                             if (sgp == null)
 199                                 sgp = new Surrogate.Parser();
 200                             if (sgp.parse(c, src) < 0)
 201                                 return sgp.error();
 202                             return sgp.unmappableResult();
 203                         }
 204                         return CoderResult.unmappableForLength(1);
 205                     }
 206                     if (!dst.hasRemaining())
 207                         return CoderResult.OVERFLOW;
 208                     dst.put((byte)b);
 209                     mark++;
 210                 }
 211                 return CoderResult.UNDERFLOW;
 212             } finally {
 213                 src.position(mark);
 214             }
 215         }
 216 
 217         protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
 218             if (src.hasArray() && dst.hasArray())
 219                 return encodeArrayLoop(src, dst);
 220             else
 221                 return encodeBufferLoop(src, dst);
 222         }
 223 
 224         public final int encode(char ch) {
 225             char index = c2bIndex[ch >> 8];
 226             if (index == UNMAPPABLE_ENCODING)
 227                 return UNMAPPABLE_ENCODING;
 228             return c2b[index + (ch & 0xff)];
 229         }
 230 
 231         private byte repl = (byte)'?';
 232         protected void implReplaceWith(byte[] newReplacement) {
 233             repl = newReplacement[0];
 234         }
 235 
 236         public int encode(char[] src, int sp, int len, byte[] dst) {
 237             int dp = 0;
 238             int sl = sp + Math.min(len, dst.length);
 239             while (sp < sl) {
 240                 char c = src[sp++];
 241                 int b = encode(c);
 242                 if (b != UNMAPPABLE_ENCODING) {
 243                     dst[dp++] = (byte)b;
 244                     continue;
 245                 }
 246                 if (Character.isHighSurrogate(c) && sp < sl &&
 247                     Character.isLowSurrogate(src[sp])) {
 248                     if (len > dst.length) {
 249                         sl++;
 250                         len--;
 251                     }
 252                     sp++;
 253                 }
 254                 dst[dp++] = repl;
 255             }
 256             return dp;
 257         }
 258     }
 259 
 260     // init the c2b and c2bIndex tables from b2c.
 261     public static void initC2B(char[] b2c, char[] c2bNR,
 262                                char[] c2b, char[] c2bIndex) {
 263         for (int i = 0; i < c2bIndex.length; i++)
 264             c2bIndex[i] = UNMAPPABLE_ENCODING;
 265         for (int i = 0; i < c2b.length; i++)
 266             c2b[i] = UNMAPPABLE_ENCODING;
 267         int off = 0;
 268         for (int i = 0; i < b2c.length; i++) {
 269             char c = b2c[i];
 270             if (c == UNMAPPABLE_DECODING)
 271                 continue;
 272             int index = (c >> 8);
 273             if (c2bIndex[index] == UNMAPPABLE_ENCODING) {
 274                 c2bIndex[index] = (char)off;
 275                 off += 0x100;
 276             }
 277             index = c2bIndex[index] + (c & 0xff);
 278             c2b[index] = (char)((i>=0x80)?(i-0x80):(i+0x80));
 279         }
 280         if (c2bNR != null) {
 281             // c-->b nr entries
 282             int i = 0;
 283             while (i < c2bNR.length) {
 284                 char b = c2bNR[i++];
 285                 char c = c2bNR[i++];
 286                 int index = (c >> 8);
 287                 if (c2bIndex[index] == UNMAPPABLE_ENCODING) {
 288                     c2bIndex[index] = (char)off;
 289                     off += 0x100;
 290                 }
 291                 index = c2bIndex[index] + (c & 0xff);
 292                 c2b[index] = b;
 293             }
 294         }
 295     }
 296 }