History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/lang/StringCoding.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/lang/StringCoding.java+402−0
@@ -0,0 +1,402 @@
1/*
2 * Copyright (c) 2000, 2011, 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
26package java.lang;
27
28import java.io.UnsupportedEncodingException;
29import java.lang.ref.SoftReference;
30import java.nio.ByteBuffer;
31import java.nio.CharBuffer;
32import java.nio.charset.Charset;
33import java.nio.charset.CharsetDecoder;
34import java.nio.charset.CharsetEncoder;
35import java.nio.charset.CharacterCodingException;
36import java.nio.charset.CoderResult;
37import java.nio.charset.CodingErrorAction;
38import java.nio.charset.IllegalCharsetNameException;
39import java.nio.charset.UnsupportedCharsetException;
40import java.util.Arrays;
41import sun.misc.MessageUtils;
42import sun.nio.cs.HistoricallyNamedCharset;
43import sun.nio.cs.ArrayDecoder;
44import sun.nio.cs.ArrayEncoder;
45
46/**
47 * Utility class for string encoding and decoding.
48 */
49
50class StringCoding {
51
52 private StringCoding() { }
53
54 /** The cached coders for each thread */
55 private final static ThreadLocal<SoftReference<StringDecoder>> decoder =
56 new ThreadLocal<>();
57 private final static ThreadLocal<SoftReference<StringEncoder>> encoder =
58 new ThreadLocal<>();
59
60 private static boolean warnUnsupportedCharset = true;
61
62 private static <T> T deref(ThreadLocal<SoftReference<T>> tl) {
63 SoftReference<T> sr = tl.get();
64 if (sr == null)
65 return null;
66 return sr.get();
67 }
68
69 private static <T> void set(ThreadLocal<SoftReference<T>> tl, T ob) {
70 tl.set(new SoftReference<T>(ob));
71 }
72
73 // Trim the given byte array to the given length
74 //
75 private static byte[] safeTrim(byte[] ba, int len, Charset cs, boolean isTrusted) {
76 if (len == ba.length && (isTrusted || System.getSecurityManager() == null))
77 return ba;
78 else
79 return Arrays.copyOf(ba, len);
80 }
81
82 // Trim the given char array to the given length
83 //
84 private static char[] safeTrim(char[] ca, int len,
85 Charset cs, boolean isTrusted) {
86 if (len == ca.length && (isTrusted || System.getSecurityManager() == null))
87 return ca;
88 else
89 return Arrays.copyOf(ca, len);
90 }
91
92 private static int scale(int len, float expansionFactor) {
93 // We need to perform double, not float, arithmetic; otherwise
94 // we lose low order bits when len is larger than 2**24.
95 return (int)(len * (double)expansionFactor);
96 }
97
98 private static Charset lookupCharset(String csn) {
99 if (Charset.isSupported(csn)) {
100 try {
101 return Charset.forName(csn);
102 } catch (UnsupportedCharsetException x) {
103 throw new Error(x);
104 }
105 }
106 return null;
107 }
108
109 private static void warnUnsupportedCharset(String csn) {
110 if (warnUnsupportedCharset) {
111 // Use sun.misc.MessageUtils rather than the Logging API or
112 // System.err since this method may be called during VM
113 // initialization before either is available.
114 MessageUtils.err("WARNING: Default charset " + csn +
115 " not supported, using ISO-8859-1 instead");
116 warnUnsupportedCharset = false;
117 }
118 }
119
120
121 // -- Decoding --
122 private static class StringDecoder {
123 private final String requestedCharsetName;
124 private final Charset cs;
125 private final CharsetDecoder cd;
126 private final boolean isTrusted;
127
128 private StringDecoder(Charset cs, String rcn) {
129 this.requestedCharsetName = rcn;
130 this.cs = cs;
131 this.cd = cs.newDecoder()
132 .onMalformedInput(CodingErrorAction.REPLACE)
133 .onUnmappableCharacter(CodingErrorAction.REPLACE);
134 this.isTrusted = (cs.getClass().getClassLoader0() == null);
135 }
136
137 String charsetName() {
138 if (cs instanceof HistoricallyNamedCharset)
139 return ((HistoricallyNamedCharset)cs).historicalName();
140 return cs.name();
141 }
142
143 final String requestedCharsetName() {
144 return requestedCharsetName;
145 }
146
147 char[] decode(byte[] ba, int off, int len) {
148 int en = scale(len, cd.maxCharsPerByte());
149 char[] ca = new char[en];
150 if (len == 0)
151 return ca;
152 if (cd instanceof ArrayDecoder) {
153 int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
154 return safeTrim(ca, clen, cs, isTrusted);
155 } else {
156 cd.reset();
157 ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
158 CharBuffer cb = CharBuffer.wrap(ca);
159 try {
160 CoderResult cr = cd.decode(bb, cb, true);
161 if (!cr.isUnderflow())
162 cr.throwException();
163 cr = cd.flush(cb);
164 if (!cr.isUnderflow())
165 cr.throwException();
166 } catch (CharacterCodingException x) {
167 // Substitution is always enabled,
168 // so this shouldn't happen
169 throw new Error(x);
170 }
171 return safeTrim(ca, cb.position(), cs, isTrusted);
172 }
173 }
174 }
175
176 static char[] decode(String charsetName, byte[] ba, int off, int len)
177 throws UnsupportedEncodingException
178 {
179 StringDecoder sd = deref(decoder);
180 String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
181 if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
182 || csn.equals(sd.charsetName()))) {
183 sd = null;
184 try {
185 Charset cs = lookupCharset(csn);
186 if (cs != null)
187 sd = new StringDecoder(cs, csn);
188 } catch (IllegalCharsetNameException x) {}
189 if (sd == null)
190 throw new UnsupportedEncodingException(csn);
191 set(decoder, sd);
192 }
193 return sd.decode(ba, off, len);
194 }
195
196 static char[] decode(Charset cs, byte[] ba, int off, int len) {
197 // (1)We never cache the "external" cs, the only benefit of creating
198 // an additional StringDe/Encoder object to wrap it is to share the
199 // de/encode() method. These SD/E objects are short-lifed, the young-gen
200 // gc should be able to take care of them well. But the best approash
201 // is still not to generate them if not really necessary.
202 // (2)The defensive copy of the input byte/char[] has a big performance
203 // impact, as well as the outgoing result byte/char[]. Need to do the
204 // optimization check of (sm==null && classLoader0==null) for both.
205 // (3)getClass().getClassLoader0() is expensive
206 // (4)There might be a timing gap in isTrusted setting. getClassLoader0()
207 // is only chcked (and then isTrusted gets set) when (SM==null). It is
208 // possible that the SM==null for now but then SM is NOT null later
209 // when safeTrim() is invoked...the "safe" way to do is to redundant
210 // check (... && (isTrusted || SM == null || getClassLoader0())) in trim
211 // but it then can be argued that the SM is null when the opertaion
212 // is started...
213 CharsetDecoder cd = cs.newDecoder();
214 int en = scale(len, cd.maxCharsPerByte());
215 char[] ca = new char[en];
216 if (len == 0)
217 return ca;
218 boolean isTrusted = false;
219 if (System.getSecurityManager() != null) {
220 if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
221 ba = Arrays.copyOfRange(ba, off, off + len);
222 off = 0;
223 }
224 }
225 cd.onMalformedInput(CodingErrorAction.REPLACE)
226 .onUnmappableCharacter(CodingErrorAction.REPLACE)
227 .reset();
228 if (cd instanceof ArrayDecoder) {
229 int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
230 return safeTrim(ca, clen, cs, isTrusted);
231 } else {
232 ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
233 CharBuffer cb = CharBuffer.wrap(ca);
234 try {
235 CoderResult cr = cd.decode(bb, cb, true);
236 if (!cr.isUnderflow())
237 cr.throwException();
238 cr = cd.flush(cb);
239 if (!cr.isUnderflow())
240 cr.throwException();
241 } catch (CharacterCodingException x) {
242 // Substitution is always enabled,
243 // so this shouldn't happen
244 throw new Error(x);
245 }
246 return safeTrim(ca, cb.position(), cs, isTrusted);
247 }
248 }
249
250 static char[] decode(byte[] ba, int off, int len) {
251 String csn = Charset.defaultCharset().name();
252 try {
253 return decode(csn, ba, off, len);
254 } catch (UnsupportedEncodingException x) {
255 warnUnsupportedCharset(csn);
256 }
257 try {
258 return decode("ISO-8859-1", ba, off, len);
259 } catch (UnsupportedEncodingException x) {
260 // If this code is hit during VM initialization, MessageUtils is
261 // the only way we will be able to get any kind of error message.
262 MessageUtils.err("ISO-8859-1 charset not available: "
263 + x.toString());
264 // If we can not find ISO-8859-1 (a required encoding) then things
265 // are seriously wrong with the installation.
266 System.exit(1);
267 return null;
268 }
269 }
270
271 // -- Encoding --
272 private static class StringEncoder {
273 private Charset cs;
274 private CharsetEncoder ce;
275 private final String requestedCharsetName;
276 private final boolean isTrusted;
277
278 private StringEncoder(Charset cs, String rcn) {
279 this.requestedCharsetName = rcn;
280 this.cs = cs;
281 this.ce = cs.newEncoder()
282 .onMalformedInput(CodingErrorAction.REPLACE)
283 .onUnmappableCharacter(CodingErrorAction.REPLACE);
284 this.isTrusted = (cs.getClass().getClassLoader0() == null);
285 }
286
287 String charsetName() {
288 if (cs instanceof HistoricallyNamedCharset)
289 return ((HistoricallyNamedCharset)cs).historicalName();
290 return cs.name();
291 }
292
293 final String requestedCharsetName() {
294 return requestedCharsetName;
295 }
296
297 byte[] encode(char[] ca, int off, int len) {
298 int en = scale(len, ce.maxBytesPerChar());
299 byte[] ba = new byte[en];
300 if (len == 0)
301 return ba;
302 if (ce instanceof ArrayEncoder) {
303 int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
304 return safeTrim(ba, blen, cs, isTrusted);
305 } else {
306 ce.reset();
307 ByteBuffer bb = ByteBuffer.wrap(ba);
308 CharBuffer cb = CharBuffer.wrap(ca, off, len);
309 try {
310 CoderResult cr = ce.encode(cb, bb, true);
311 if (!cr.isUnderflow())
312 cr.throwException();
313 cr = ce.flush(bb);
314 if (!cr.isUnderflow())
315 cr.throwException();
316 } catch (CharacterCodingException x) {
317 // Substitution is always enabled,
318 // so this shouldn't happen
319 throw new Error(x);
320 }
321 return safeTrim(ba, bb.position(), cs, isTrusted);
322 }
323 }
324 }
325
326 static byte[] encode(String charsetName, char[] ca, int off, int len)
327 throws UnsupportedEncodingException
328 {
329 StringEncoder se = deref(encoder);
330 String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
331 if ((se == null) || !(csn.equals(se.requestedCharsetName())
332 || csn.equals(se.charsetName()))) {
333 se = null;
334 try {
335 Charset cs = lookupCharset(csn);
336 if (cs != null)
337 se = new StringEncoder(cs, csn);
338 } catch (IllegalCharsetNameException x) {}
339 if (se == null)
340 throw new UnsupportedEncodingException (csn);
341 set(encoder, se);
342 }
343 return se.encode(ca, off, len);
344 }
345
346 static byte[] encode(Charset cs, char[] ca, int off, int len) {
347 CharsetEncoder ce = cs.newEncoder();
348 int en = scale(len, ce.maxBytesPerChar());
349 byte[] ba = new byte[en];
350 if (len == 0)
351 return ba;
352 boolean isTrusted = false;
353 if (System.getSecurityManager() != null) {
354 if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
355 ca = Arrays.copyOfRange(ca, off, off + len);
356 off = 0;
357 }
358 }
359 ce.onMalformedInput(CodingErrorAction.REPLACE)
360 .onUnmappableCharacter(CodingErrorAction.REPLACE)
361 .reset();
362 if (ce instanceof ArrayEncoder) {
363 int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
364 return safeTrim(ba, blen, cs, isTrusted);
365 } else {
366 ByteBuffer bb = ByteBuffer.wrap(ba);
367 CharBuffer cb = CharBuffer.wrap(ca, off, len);
368 try {
369 CoderResult cr = ce.encode(cb, bb, true);
370 if (!cr.isUnderflow())
371 cr.throwException();
372 cr = ce.flush(bb);
373 if (!cr.isUnderflow())
374 cr.throwException();
375 } catch (CharacterCodingException x) {
376 throw new Error(x);
377 }
378 return safeTrim(ba, bb.position(), cs, isTrusted);
379 }
380 }
381
382 static byte[] encode(char[] ca, int off, int len) {
383 String csn = Charset.defaultCharset().name();
384 try {
385 return encode(csn, ca, off, len);
386 } catch (UnsupportedEncodingException x) {
387 warnUnsupportedCharset(csn);
388 }
389 try {
390 return encode("ISO-8859-1", ca, off, len);
391 } catch (UnsupportedEncodingException x) {
392 // If this code is hit during VM initialization, MessageUtils is
393 // the only way we will be able to get any kind of error message.
394 MessageUtils.err("ISO-8859-1 charset not available: "
395 + x.toString());
396 // If we can not find ISO-8859-1 (a required encoding) then things
397 // are seriously wrong with the installation.
398 System.exit(1);
399 return null;
400 }
401 }
402}