TextRange (@NotNull CharBuffer in, int position, @NotNull Ref<ByteBuffer> outRef, @NotNull CharBuffer back, @NotNull Charset charset) { CharsetEncoder encoder = charset.newEncoder() .onUnmappableCharacter(CodingErrorAction.REPORT) .onMalformedInput(CodingErrorAction.REPORT); int textLength = in.limit() - position; ByteBuffer out = outRef.get(); if (out == null) { outRef.set(out = ByteBuffer.allocate((int)(encoder.averageBytesPerChar() * textLength))); } out.rewind(); out.limit(out.capacity()); in.rewind(); in.position(position); CoderResult cr; for (;;) { cr = in.hasRemaining() ? encoder.encode(in, out, true) : CoderResult.UNDERFLOW; if (cr.isUnderflow()) { cr = encoder.flush(out); } if (!cr.isOverflow()) { break; } int n = 3 * out.capacity()/2 + 1; ByteBuffer tmp = ByteBuffer.allocate(n); out.flip(); tmp.put(out); outRef.set(out = tmp); } if (cr.isError()) { return TextRange.from(in.position(), cr.length()); } // phew, encoded successfully. now check if we can decode it back with char-to-char precision int outLength = out.position(); CharsetDecoder decoder = charset.newDecoder() .onUnmappableCharacter(CodingErrorAction.REPORT) .onMalformedInput(CodingErrorAction.REPORT); out.rewind(); out.limit(outLength); back.rewind(); CoderResult dr = decoder.decode(out, back, true); if (dr.isError()) { return TextRange.from(back.position(), dr.length()); } if (back.position() != textLength) { return TextRange.from(Math.min(textLength, back.position()), 1); } // ok, we decoded it back to string. now compare if the strings are identical in.rewind(); in.position(position); back.rewind(); int len = StringUtil.commonPrefixLength(in, back); if (len == textLength) return null; return TextRange.from(len, 1); // let's report only the first diff char }