init
This commit is contained in:
284
java/org/apache/tomcat/util/buf/B2CConverter.java
Normal file
284
java/org/apache/tomcat/util/buf/B2CConverter.java
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* NIO based character decoder.
|
||||
*/
|
||||
public class B2CConverter {
|
||||
|
||||
private static final StringManager sm =
|
||||
StringManager.getManager(Constants.Package);
|
||||
|
||||
private static final CharsetCache charsetCache = new CharsetCache();
|
||||
|
||||
|
||||
// Protected so unit tests can use it
|
||||
protected static final int LEFTOVER_SIZE = 9;
|
||||
|
||||
/**
|
||||
* Obtain the Charset for the given encoding
|
||||
*
|
||||
* @param enc The name of the encoding for the required charset
|
||||
*
|
||||
* @return The Charset corresponding to the requested encoding
|
||||
*
|
||||
* @throws UnsupportedEncodingException If the requested Charset is not
|
||||
* available
|
||||
*/
|
||||
public static Charset getCharset(String enc)
|
||||
throws UnsupportedEncodingException {
|
||||
|
||||
// Encoding names should all be ASCII
|
||||
String lowerCaseEnc = enc.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
return getCharsetLower(lowerCaseEnc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Only to be used when it is known that the encoding name is in lower case.
|
||||
* @param lowerCaseEnc The name of the encoding for the required charset in
|
||||
* lower case
|
||||
*
|
||||
* @return The Charset corresponding to the requested encoding
|
||||
*
|
||||
* @throws UnsupportedEncodingException If the requested Charset is not
|
||||
* available
|
||||
*
|
||||
* @deprecated Will be removed in Tomcat 9.0.x
|
||||
*/
|
||||
@Deprecated
|
||||
public static Charset getCharsetLower(String lowerCaseEnc)
|
||||
throws UnsupportedEncodingException {
|
||||
|
||||
Charset charset = charsetCache.getCharset(lowerCaseEnc);
|
||||
|
||||
if (charset == null) {
|
||||
// Pre-population of the cache means this must be invalid
|
||||
throw new UnsupportedEncodingException(
|
||||
sm.getString("b2cConverter.unknownEncoding", lowerCaseEnc));
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
|
||||
private final CharsetDecoder decoder;
|
||||
private ByteBuffer bb = null;
|
||||
private CharBuffer cb = null;
|
||||
|
||||
/**
|
||||
* Leftover buffer used for incomplete characters.
|
||||
*/
|
||||
private final ByteBuffer leftovers;
|
||||
|
||||
public B2CConverter(Charset charset) {
|
||||
this(charset, false);
|
||||
}
|
||||
|
||||
public B2CConverter(Charset charset, boolean replaceOnError) {
|
||||
byte[] left = new byte[LEFTOVER_SIZE];
|
||||
leftovers = ByteBuffer.wrap(left);
|
||||
CodingErrorAction action;
|
||||
if (replaceOnError) {
|
||||
action = CodingErrorAction.REPLACE;
|
||||
} else {
|
||||
action = CodingErrorAction.REPORT;
|
||||
}
|
||||
// Special case. Use the Apache Harmony based UTF-8 decoder because it
|
||||
// - a) rejects invalid sequences that the JVM decoder does not
|
||||
// - b) fails faster for some invalid sequences
|
||||
if (charset.equals(StandardCharsets.UTF_8)) {
|
||||
decoder = new Utf8Decoder();
|
||||
} else {
|
||||
decoder = charset.newDecoder();
|
||||
}
|
||||
decoder.onMalformedInput(action);
|
||||
decoder.onUnmappableCharacter(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the decoder state.
|
||||
*/
|
||||
public void recycle() {
|
||||
decoder.reset();
|
||||
leftovers.position(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given bytes to characters.
|
||||
*
|
||||
* @param bc byte input
|
||||
* @param cc char output
|
||||
* @param endOfInput Is this all of the available data
|
||||
*
|
||||
* @throws IOException If the conversion can not be completed
|
||||
*/
|
||||
public void convert(ByteChunk bc, CharChunk cc, boolean endOfInput)
|
||||
throws IOException {
|
||||
if ((bb == null) || (bb.array() != bc.getBuffer())) {
|
||||
// Create a new byte buffer if anything changed
|
||||
bb = ByteBuffer.wrap(bc.getBuffer(), bc.getStart(), bc.getLength());
|
||||
} else {
|
||||
// Initialize the byte buffer
|
||||
bb.limit(bc.getEnd());
|
||||
bb.position(bc.getStart());
|
||||
}
|
||||
if ((cb == null) || (cb.array() != cc.getBuffer())) {
|
||||
// Create a new char buffer if anything changed
|
||||
cb = CharBuffer.wrap(cc.getBuffer(), cc.getEnd(),
|
||||
cc.getBuffer().length - cc.getEnd());
|
||||
} else {
|
||||
// Initialize the char buffer
|
||||
cb.limit(cc.getBuffer().length);
|
||||
cb.position(cc.getEnd());
|
||||
}
|
||||
CoderResult result = null;
|
||||
// Parse leftover if any are present
|
||||
if (leftovers.position() > 0) {
|
||||
int pos = cb.position();
|
||||
// Loop until one char is decoded or there is a decoder error
|
||||
do {
|
||||
leftovers.put(bc.substractB());
|
||||
leftovers.flip();
|
||||
result = decoder.decode(leftovers, cb, endOfInput);
|
||||
leftovers.position(leftovers.limit());
|
||||
leftovers.limit(leftovers.array().length);
|
||||
} while (result.isUnderflow() && (cb.position() == pos));
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
}
|
||||
bb.position(bc.getStart());
|
||||
leftovers.position(0);
|
||||
}
|
||||
// Do the decoding and get the results into the byte chunk and the char
|
||||
// chunk
|
||||
result = decoder.decode(bb, cb, endOfInput);
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
} else if (result.isOverflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk, if
|
||||
// this continues the char buffer will get resized
|
||||
bc.setOffset(bb.position());
|
||||
cc.setEnd(cb.position());
|
||||
} else if (result.isUnderflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.setOffset(bb.position());
|
||||
cc.setEnd(cb.position());
|
||||
// Put leftovers in the leftovers byte buffer
|
||||
if (bc.getLength() > 0) {
|
||||
leftovers.limit(leftovers.array().length);
|
||||
leftovers.position(bc.getLength());
|
||||
bc.substract(leftovers.array(), 0, bc.getLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given bytes to characters.
|
||||
*
|
||||
* @param bc byte input
|
||||
* @param cc char output
|
||||
* @param ic byte input channel
|
||||
* @param endOfInput Is this all of the available data
|
||||
*
|
||||
* @throws IOException If the conversion can not be completed
|
||||
*/
|
||||
public void convert(ByteBuffer bc, CharBuffer cc, ByteChunk.ByteInputChannel ic, boolean endOfInput)
|
||||
throws IOException {
|
||||
if ((bb == null) || (bb.array() != bc.array())) {
|
||||
// Create a new byte buffer if anything changed
|
||||
bb = ByteBuffer.wrap(bc.array(), bc.arrayOffset() + bc.position(), bc.remaining());
|
||||
} else {
|
||||
// Initialize the byte buffer
|
||||
bb.limit(bc.limit());
|
||||
bb.position(bc.position());
|
||||
}
|
||||
if ((cb == null) || (cb.array() != cc.array())) {
|
||||
// Create a new char buffer if anything changed
|
||||
cb = CharBuffer.wrap(cc.array(), cc.limit(), cc.capacity() - cc.limit());
|
||||
} else {
|
||||
// Initialize the char buffer
|
||||
cb.limit(cc.capacity());
|
||||
cb.position(cc.limit());
|
||||
}
|
||||
CoderResult result = null;
|
||||
// Parse leftover if any are present
|
||||
if (leftovers.position() > 0) {
|
||||
int pos = cb.position();
|
||||
// Loop until one char is decoded or there is a decoder error
|
||||
do {
|
||||
byte chr;
|
||||
if (bc.remaining() == 0) {
|
||||
int n = ic.realReadBytes();
|
||||
chr = n < 0 ? -1 : bc.get();
|
||||
} else {
|
||||
chr = bc.get();
|
||||
}
|
||||
leftovers.put(chr);
|
||||
leftovers.flip();
|
||||
result = decoder.decode(leftovers, cb, endOfInput);
|
||||
leftovers.position(leftovers.limit());
|
||||
leftovers.limit(leftovers.array().length);
|
||||
} while (result.isUnderflow() && (cb.position() == pos));
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
}
|
||||
bb.position(bc.position());
|
||||
leftovers.position(0);
|
||||
}
|
||||
// Do the decoding and get the results into the byte chunk and the char
|
||||
// chunk
|
||||
result = decoder.decode(bb, cb, endOfInput);
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
} else if (result.isOverflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk, if
|
||||
// this continues the char buffer will get resized
|
||||
bc.position(bb.position());
|
||||
cc.limit(cb.position());
|
||||
} else if (result.isUnderflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.position(bb.position());
|
||||
cc.limit(cb.position());
|
||||
// Put leftovers in the leftovers byte buffer
|
||||
if (bc.remaining() > 0) {
|
||||
leftovers.limit(leftovers.array().length);
|
||||
leftovers.position(bc.remaining());
|
||||
bc.get(leftovers.array(), 0, bc.remaining());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Charset getCharset() {
|
||||
return decoder.charset();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user