Source:
<code>public String unpackString() throws IOException { int strLen = unpackRawStringHeader(); if(strLen > 0) { if(strLen > config.getMaxUnpackStringSize()) { throw new MessageSizeException(String.format("cannot unpack a String of size larger than %,d: %,d", config.getMaxUnpackStringSize(), strLen), strLen); } prepareDecoder(); assert(decoder != null); decoder.reset(); try { int cursor = 0; decodeBuffer.clear(); StringBuilder sb = new StringBuilder(); while(cursor < strLen) { if (!ensure(strLen-cursor)) throw new EOFException(); <START> int readLen = Math.min(buffer.size() - position, strLen-cursor); <END> ByteBuffer bb = buffer.toByteBuffer(position, readLen); while(bb.hasRemaining()) { boolean endOfInput = (cursor + readLen) >= strLen; CoderResult cr = decoder.decode(bb, decodeBuffer, endOfInput); if(endOfInput && cr.isUnderflow()) cr = decoder.flush(decodeBuffer); if(cr.isOverflow()) { decoder.reset(); } if(cr.isUnderflow() && bb.hasRemaining()) { if(config.getActionOnMalFormedInput() == CodingErrorAction.REPORT) { throw new MalformedInputException(strLen); } while (bb.hasRemaining()) bb.get(); } if(cr.isError()) { if((cr.isMalformed() && config.getActionOnMalFormedInput() == CodingErrorAction.REPORT) || (cr.isUnmappable() && config.getActionOnUnmappableCharacter() == CodingErrorAction.REPORT)) { cr.throwException(); } } decodeBuffer.flip(); sb.append(decodeBuffer); decodeBuffer.clear(); } cursor += readLen; consume(readLen); } return sb.toString(); } catch(CharacterCodingException e) { throw new MessageStringCodingException(e); } } else return EMPTY_STRING; }</code><technical_language>ensure(int byteSizeToRead) guarantee prepare a buffer requested byteSizeToRead. readLen simply strLen - cursor? If so, remove outer loop</technical_language>

Target:
public String unpackString() throws IOException { int strLen = unpackRawStringHeader(); if(strLen > 0) { if(strLen > config.getMaxUnpackStringSize()) { throw new MessageSizeException(String.format("cannot unpack a String of size larger than %,d: %,d", config.getMaxUnpackStringSize(), strLen), strLen); } prepareDecoder(); assert(decoder != null); decoder.reset(); try { int cursor = 0; decodeBuffer.clear(); StringBuilder sb = new StringBuilder(); while(cursor < strLen) { int readLen = Math.min(position < buffer.size() ? buffer.size() - position : buffer.size(), strLen-cursor); if (!ensure(readLen)) throw new EOFException(); ByteBuffer bb = buffer.toByteBuffer(position, readLen); while(bb.hasRemaining()) { boolean endOfInput = (cursor + readLen) >= strLen; CoderResult cr = decoder.decode(bb, decodeBuffer, endOfInput); if(endOfInput && cr.isUnderflow()) cr = decoder.flush(decodeBuffer); if(cr.isOverflow()) { decoder.reset(); } if(cr.isUnderflow() && bb.hasRemaining()) { if(config.getActionOnMalFormedInput() == CodingErrorAction.REPORT) { throw new MalformedInputException(strLen); } while (bb.hasRemaining()) bb.get(); } if(cr.isError()) { if((cr.isMalformed() && config.getActionOnMalFormedInput() == CodingErrorAction.REPORT) || (cr.isUnmappable() && config.getActionOnUnmappableCharacter() == CodingErrorAction.REPORT)) { cr.throwException(); } } decodeBuffer.flip(); sb.append(decodeBuffer); decodeBuffer.clear(); } cursor += readLen; consume(readLen); } return sb.toString(); } catch(CharacterCodingException e) { throw new MessageStringCodingException(e); } } else return EMPTY_STRING; }