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 }