// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.sdk.util;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
/**
* A stateful converter from bytes to characters according to a specified {@link Charset}.
* The byte input may end with an incomplete character code (e.g. in UTF-8 one character
* is coded up to 4 bytes). In this case the partial character code is saved in an internal
* buffer, thus the statefulness.
*/
public class ByteToCharConverter {
private static final int REMAINDER_MAX_SIZE = 10;
private final CharsetDecoder decoder;
private final ByteBuffer unprocessedBuffer;
public ByteToCharConverter(Charset charset) {
decoder = charset.newDecoder();
unprocessedBuffer = ByteBuffer.wrap(new byte[REMAINDER_MAX_SIZE]);
unprocessedBuffer.flip();
}
public CharBuffer convert(ByteBuffer input) {
CharBuffer result = convertImpl(input);
result.flip();
return result;
}
private CharBuffer convertImpl(ByteBuffer input) {
int bytesCount = input.remaining() + unprocessedBuffer.remaining() + 1;
CharBuffer out = CharBuffer.allocate((int)(bytesCount*decoder.maxCharsPerByte()));
// Process what has left from previous call.
if (unprocessedBuffer.hasRemaining()) {
while (true) {
if (!input.hasRemaining()) {
return out;
}
unprocessedBuffer.compact();
unprocessedBuffer.put(input.get());
unprocessedBuffer.flip();
CoderResult res = decoder.decode(unprocessedBuffer, out, false);
if (!res.isUnderflow()) {
throw new RuntimeException("Unexpected error: " + res);
}
if (unprocessedBuffer.position() > 0) {
assert !unprocessedBuffer.hasRemaining();
break;
}
}
}
if (!input.hasRemaining()) {
return out;
}
// Process main bulk.
CoderResult res = decoder.decode(input, out, false);
if (!res.isUnderflow()) {
throw new RuntimeException("Unexpected error: " + res);
}
// Save what remained for future processing.
unprocessedBuffer.clear();
while (input.hasRemaining()) {
unprocessedBuffer.put(input.get());
}
unprocessedBuffer.flip();
return out;
}
}