/**
   * Gets the relative path of <code>file</code> to its <code>ancestor</code>. Uses <code>separator
   * </code> for separating files.
   *
   * @param file the file
   * @param ancestor parent file
   * @param separator character to use as files separator
   * @return the relative path or {@code null} if {@code ancestor} is not ancestor for {@code file}
   */
  @Nullable
  public static String getRelativePath(
      @NotNull VirtualFile file, @NotNull VirtualFile ancestor, char separator) {
    if (!file.getFileSystem().equals(ancestor.getFileSystem())) return null;

    int length = 0;
    VirtualFile parent = file;
    while (true) {
      if (parent == null) return null;
      if (parent.equals(ancestor)) break;
      if (length > 0) {
        length++;
      }
      length += parent.getName().length();
      parent = parent.getParent();
    }

    char[] chars = new char[length];
    int index = chars.length;
    parent = file;
    while (true) {
      if (parent.equals(ancestor)) break;
      if (index < length) {
        chars[--index] = separator;
      }
      String name = parent.getName();
      for (int i = name.length() - 1; i >= 0; i--) {
        chars[--index] = name.charAt(i);
      }
      parent = parent.getParent();
    }
    return StringFactory.createShared(chars);
  }
 @NotNull
 public static String loadTextAndClose(@NotNull Reader reader) throws IOException {
   try {
     return StringFactory.createShared(adaptiveLoadText(reader));
   } finally {
     reader.close();
   }
 }
 @NotNull
 public static String loadText(@NotNull VirtualFile file, int length) throws IOException {
   InputStreamReader reader = new InputStreamReader(file.getInputStream(), file.getCharset());
   try {
     return StringFactory.createShared(FileUtil.loadText(reader, length));
   } finally {
     reader.close();
   }
 }
  public static String encode(@NotNull byte[] octetString) {
    int bits24;
    int bits6;

    char[] out = new char[((octetString.length - 1) / 3 + 1) * 4];

    int outIndex = 0;
    int i = 0;

    while ((i + 3) <= octetString.length) {
      // store the octets
      bits24 = (octetString[i++] & 0xFF) << 16;
      bits24 |= (octetString[i++] & 0xFF) << 8;
      bits24 |= (octetString[i++] & 0xFF);

      bits6 = (bits24 & 0x00FC0000) >> 18;
      out[outIndex++] = alphabet[bits6];
      bits6 = (bits24 & 0x0003F000) >> 12;
      out[outIndex++] = alphabet[bits6];
      bits6 = (bits24 & 0x00000FC0) >> 6;
      out[outIndex++] = alphabet[bits6];
      bits6 = (bits24 & 0x0000003F);
      out[outIndex++] = alphabet[bits6];
    }

    if (octetString.length - i == 2) {
      // store the octets
      bits24 = (octetString[i] & 0xFF) << 16;
      bits24 |= (octetString[i + 1] & 0xFF) << 8;

      bits6 = (bits24 & 0x00FC0000) >> 18;
      out[outIndex++] = alphabet[bits6];
      bits6 = (bits24 & 0x0003F000) >> 12;
      out[outIndex++] = alphabet[bits6];
      bits6 = (bits24 & 0x00000FC0) >> 6;
      out[outIndex++] = alphabet[bits6];

      // padding
      out[outIndex] = '=';
    } else if (octetString.length - i == 1) {
      // store the octets
      bits24 = (octetString[i] & 0xFF) << 16;

      bits6 = (bits24 & 0x00FC0000) >> 18;
      out[outIndex++] = alphabet[bits6];
      bits6 = (bits24 & 0x0003F000) >> 12;
      out[outIndex++] = alphabet[bits6];

      // padding
      out[outIndex++] = '=';
      out[outIndex] = '=';
    }

    return StringFactory.createShared(out);
  }
 @NotNull
 private static String createSequence(@NotNull CharSequence text, int startOffset, int endOffset) {
   if (text instanceof String) {
     return ((String) text).substring(startOffset, endOffset);
   }
   char[] buf = new char[endOffset - startOffset];
   CharArrayUtil.getChars(text, buf, startOffset, 0, buf.length);
   return StringFactory.createShared(
       buf); // this way the .toString() doesn't create another instance (as opposed to new
             // CharArrayCharSequence())
 }