public CharSequence substring(char @NotNull [] originalData, int start, int end) {
    if (myChanges.isEmpty()) {
      return new String(originalData, start, end - start);
    }
    if (end == start) {
      return "";
    }
    
    int startChangeIndex = getChangeIndex(start);
    int endChangeIndex = getChangeIndex(end);
    
    boolean substringAffectedByChanges = startChangeIndex != endChangeIndex || startChangeIndex >= 0;
    int clientShift = 0;
    int originalStart = 0;
    if (startChangeIndex < 0) {
      startChangeIndex = -startChangeIndex - 1;
      if (startChangeIndex > 0 && startChangeIndex <= myChanges.size()) {
        ChangeEntry changeEntry = myChanges.get(startChangeIndex - 1);
        clientShift = changeEntry.clientStartOffset - changeEntry.change.getStart() + changeEntry.change.getDiff();
        originalStart = changeEntry.change.getEnd();
      }
    }
    else {
      ChangeEntry changeEntry = myChanges.get(startChangeIndex);
      clientShift = changeEntry.clientStartOffset - changeEntry.change.getStart();
    }
    
    if (!substringAffectedByChanges) {
      return new String(originalData, start - clientShift, end - start);
    }
    
    char[] data = new char[end - start];
    int outputOffset = 0;
    for (int i = startChangeIndex; i < myChanges.size() && outputOffset < data.length; i++) {
      ChangeEntry changeEntry = myChanges.get(i);
      int clientStart = changeEntry.clientStartOffset;
      if (clientStart >= end) {
        if (i == startChangeIndex) {
          return new String(originalData, start - clientShift, end - start);
        }
        System.arraycopy(originalData, originalStart, data, outputOffset, data.length - outputOffset);
        break;
      }
      int clientEnd = clientStart + changeEntry.change.getText().length();
      if (clientEnd > start) {
        if (clientStart > start) {
          int length = Math.min(clientStart - start, changeEntry.change.getStart() - originalStart);
          length = Math.min(length, data.length - outputOffset);
          System.arraycopy(originalData, changeEntry.change.getStart() - length, data, outputOffset, length);
          outputOffset += length;
          if (outputOffset >= data.length) {
            break;
          }
        }
        if (clientStart < clientEnd) {
          int changeTextStartOffset = start <= clientStart ? 0 : start - clientStart;
          int length = Math.min(clientEnd, end) - Math.max(clientStart, start);
          CharArrayUtil.getChars(changeEntry.change.getText(), data, changeTextStartOffset, outputOffset, length);
          outputOffset += length;
        }
      }
      originalStart = changeEntry.change.getEnd();
    }
    
    if (outputOffset < data.length) {
      System.arraycopy(originalData, originalStart, data, outputOffset, data.length - outputOffset);
    }
    return new String(data);
  }
public CharSequence substring(char @NotNull [] originalData, int start, int end)