193 lines
7.2 KiB
Java
193 lines
7.2 KiB
Java
/*
|
|
* 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.nio.ByteBuffer;
|
|
import java.nio.CharBuffer;
|
|
import java.nio.charset.Charset;
|
|
import java.nio.charset.CharsetEncoder;
|
|
import java.nio.charset.CoderResult;
|
|
import java.nio.charset.CodingErrorAction;
|
|
|
|
/**
|
|
* NIO based character encoder.
|
|
*/
|
|
public final class C2BConverter {
|
|
|
|
private final CharsetEncoder encoder;
|
|
private ByteBuffer bb = null;
|
|
private CharBuffer cb = null;
|
|
|
|
/**
|
|
* Leftover buffer used for multi-characters characters.
|
|
*/
|
|
private final CharBuffer leftovers;
|
|
|
|
public C2BConverter(Charset charset) {
|
|
encoder = charset.newEncoder();
|
|
encoder.onUnmappableCharacter(CodingErrorAction.REPLACE)
|
|
.onMalformedInput(CodingErrorAction.REPLACE);
|
|
char[] left = new char[4];
|
|
leftovers = CharBuffer.wrap(left);
|
|
}
|
|
|
|
/**
|
|
* Reset the encoder state.
|
|
*/
|
|
public void recycle() {
|
|
encoder.reset();
|
|
leftovers.position(0);
|
|
}
|
|
|
|
public boolean isUndeflow() {
|
|
return (leftovers.position() > 0);
|
|
}
|
|
|
|
/**
|
|
* Convert the given characters to bytes.
|
|
*
|
|
* @param cc char input
|
|
* @param bc byte output
|
|
* @throws IOException An encoding error occurred
|
|
*/
|
|
public void convert(CharChunk cc, ByteChunk bc) throws IOException {
|
|
if ((bb == null) || (bb.array() != bc.getBuffer())) {
|
|
// Create a new byte buffer if anything changed
|
|
bb = ByteBuffer.wrap(bc.getBuffer(), bc.getEnd(), bc.getBuffer().length - bc.getEnd());
|
|
} else {
|
|
// Initialize the byte buffer
|
|
bb.limit(bc.getBuffer().length);
|
|
bb.position(bc.getEnd());
|
|
}
|
|
if ((cb == null) || (cb.array() != cc.getBuffer())) {
|
|
// Create a new char buffer if anything changed
|
|
cb = CharBuffer.wrap(cc.getBuffer(), cc.getStart(), cc.getLength());
|
|
} else {
|
|
// Initialize the char buffer
|
|
cb.limit(cc.getEnd());
|
|
cb.position(cc.getStart());
|
|
}
|
|
CoderResult result = null;
|
|
// Parse leftover if any are present
|
|
if (leftovers.position() > 0) {
|
|
int pos = bb.position();
|
|
// Loop until one char is encoded or there is a encoder error
|
|
do {
|
|
leftovers.put((char) cc.substract());
|
|
leftovers.flip();
|
|
result = encoder.encode(leftovers, bb, false);
|
|
leftovers.position(leftovers.limit());
|
|
leftovers.limit(leftovers.array().length);
|
|
} while (result.isUnderflow() && (bb.position() == pos));
|
|
if (result.isError() || result.isMalformed()) {
|
|
result.throwException();
|
|
}
|
|
cb.position(cc.getStart());
|
|
leftovers.position(0);
|
|
}
|
|
// Do the decoding and get the results into the byte chunk and the char
|
|
// chunk
|
|
result = encoder.encode(cb, bb, false);
|
|
if (result.isError() || result.isMalformed()) {
|
|
result.throwException();
|
|
} else if (result.isOverflow()) {
|
|
// Propagate current positions to the byte chunk and char chunk
|
|
bc.setEnd(bb.position());
|
|
cc.setOffset(cb.position());
|
|
} else if (result.isUnderflow()) {
|
|
// Propagate current positions to the byte chunk and char chunk
|
|
bc.setEnd(bb.position());
|
|
cc.setOffset(cb.position());
|
|
// Put leftovers in the leftovers char buffer
|
|
if (cc.getLength() > 0) {
|
|
leftovers.limit(leftovers.array().length);
|
|
leftovers.position(cc.getLength());
|
|
cc.substract(leftovers.array(), 0, cc.getLength());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert the given characters to bytes.
|
|
*
|
|
* @param cc char input
|
|
* @param bc byte output
|
|
* @throws IOException An encoding error occurred
|
|
*/
|
|
public void convert(CharBuffer cc, ByteBuffer bc) throws IOException {
|
|
if ((bb == null) || (bb.array() != bc.array())) {
|
|
// Create a new byte buffer if anything changed
|
|
bb = ByteBuffer.wrap(bc.array(), bc.limit(), bc.capacity() - bc.limit());
|
|
} else {
|
|
// Initialize the byte buffer
|
|
bb.limit(bc.capacity());
|
|
bb.position(bc.limit());
|
|
}
|
|
if ((cb == null) || (cb.array() != cc.array())) {
|
|
// Create a new char buffer if anything changed
|
|
cb = CharBuffer.wrap(cc.array(), cc.arrayOffset() + cc.position(), cc.remaining());
|
|
} else {
|
|
// Initialize the char buffer
|
|
cb.limit(cc.limit());
|
|
cb.position(cc.position());
|
|
}
|
|
CoderResult result = null;
|
|
// Parse leftover if any are present
|
|
if (leftovers.position() > 0) {
|
|
int pos = bb.position();
|
|
// Loop until one char is encoded or there is a encoder error
|
|
do {
|
|
leftovers.put(cc.get());
|
|
leftovers.flip();
|
|
result = encoder.encode(leftovers, bb, false);
|
|
leftovers.position(leftovers.limit());
|
|
leftovers.limit(leftovers.array().length);
|
|
} while (result.isUnderflow() && (bb.position() == pos));
|
|
if (result.isError() || result.isMalformed()) {
|
|
result.throwException();
|
|
}
|
|
cb.position(cc.position());
|
|
leftovers.position(0);
|
|
}
|
|
// Do the decoding and get the results into the byte chunk and the char
|
|
// chunk
|
|
result = encoder.encode(cb, bb, false);
|
|
if (result.isError() || result.isMalformed()) {
|
|
result.throwException();
|
|
} else if (result.isOverflow()) {
|
|
// Propagate current positions to the byte chunk and char chunk
|
|
bc.limit(bb.position());
|
|
cc.position(cb.position());
|
|
} else if (result.isUnderflow()) {
|
|
// Propagate current positions to the byte chunk and char chunk
|
|
bc.limit(bb.position());
|
|
cc.position(cb.position());
|
|
// Put leftovers in the leftovers char buffer
|
|
if (cc.remaining() > 0) {
|
|
leftovers.limit(leftovers.array().length);
|
|
leftovers.position(cc.remaining());
|
|
cc.get(leftovers.array(), 0, cc.remaining());
|
|
}
|
|
}
|
|
}
|
|
|
|
public Charset getCharset() {
|
|
return encoder.charset();
|
|
}
|
|
}
|