docs: public static String readUTF( DataInput dis,
                                    int len ) throws IOException {
          CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
          BufferCache bufferCache = getBufferCache();
          ByteBuffer byteBuf = bufferCache.getByteBuffer(BufferCache.MINIMUM_SIZE);
          CharBuffer charBuf = bufferCache.getCharBuffer(BufferCache.MINIMUM_SIZE);
          try {
              byte[] bytes = byteBuf.array();
              while (len != 0 || byteBuf.position() > 0) {
                  // Need to read more bytes ...
                  if (len < 0) {
                      // Read until we come across the zero-byte (or until we fill up 'byteBuf') ...
                      while (byteBuf.remaining() != 0) {
                          byte b = dis.readByte();
                          if (b == 0x0) {
                              len = 0; // no more bytes to read ...
                              break;
                          }
                          byteBuf.put(b);
                      }
                      // Prepare byteBuf for reading ...
                      byteBuf.flip();
                  } else if (len == 0) {
                      // Don't read anything, but prepare the byteBuf for reading ...
                      byteBuf.flip();
                  } else {
                      // We know exactly how much we should read ...
                      int amountToRead = Math.min(len, Math.min(byteBuf.remaining(), charBuf.remaining()));
                      int offset = byteBuf.position(); // may have already read some bytes ...
                      dis.readFully(bytes, offset, amountToRead);
                      // take into account the offset because we might have carry-over bytes from a previous compaction 
                      // this happens when decoding multi-byte UTF8 chars
                      byteBuf.limit(amountToRead + offset); 
                      byteBuf.rewind();
                      // Adjust the number of bytes to read ...
                      len -= amountToRead;
                  }
  
                  // We've either read all we need to or as much as we can (given the buffer's limited size),
                  // so decode what we've read ...
                  boolean endOfInput = len == 0;
                  CoderResult result = decoder.decode(byteBuf, charBuf, endOfInput);
                  if (result.isError()) {
                      result.throwException();
                  } else if (result.isUnderflow()) {
                      // We've not read enough bytes yet, so move the bytes that weren't read to the beginning of the buffer
                      byteBuf.compact();
                      // and try again ...
                  }
                  if (len > 0 && (charBuf.remaining() == 0 || result.isOverflow())) {
                      // the output buffer was too small or is at its end.
                      // allocate a new one which need to have enough capacity to hold the current one (we're appending buffers) and also to hold
                      // the data from the new iteration.
                      int newBufferIncrement = (len >  BufferCache.MINIMUM_SIZE) ?  BufferCache.MINIMUM_SIZE : len;
                      CharBuffer newBuffer = bufferCache.getCharBuffer(charBuf.capacity() + newBufferIncrement);
                      // prepare the old buffer for reading ...
                      charBuf.flip();
                      // and copy the contents ...
                      newBuffer.put(charBuf);
                      // and use the new buffer ...
                      charBuf = newBuffer;
                  }
              }
              // We're done, so prepare the character buffer for reading and then convert to a String ...
              charBuf.flip();
              return charBuf.toString();
          } finally {
              // Return the buffers to the cache ...
              bufferCache.checkin(byteBuf);
              bufferCache.checkin(charBuf);
          }
      }