// 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; } }