public void mergeInPlace(char @NotNull [] data, int length, @NotNull List<? extends TextChangeImpl> changes)
    throws IllegalArgumentException
  {
    // Consider two corner cases:
    //     1. Every given change increases text length, i.e. change text length is more than changed region length. We can calculate
    //        resulting text length and start merging the changes from the right end then;
    //     2. Every given change reduces text length, start from the left end then;
    // The general idea is to group all of the given changes by 'add text'/ 'remove text' criteria and process them sequentially.
    // Example: let's assume we have the following changes:
    //     1) replace two symbols with five (diff +3);
    //     2) replace two symbols by one (diff -1);
    //     3) replace two symbols by one (diff -1);
    //     4) replace four symbols by one (diff -3);
    //     5) replace one symbol by two (diff +2);
    //     6) replace one symbol by three (diff +2);
    // Algorithm:
    //     1. Define the first group of change. First change diff is '+3', hence, iterate all changes until the resulting diff becomes
    //        less or equal to the zero. So, the first four changes conduct the first group. Initial change increased text length, hence,
    //        we process the changes from right to left starting at offset '4-th change start + 1';
    //     2. Current diff is '-2' (4-th change diff is '-3' and one slot was necessary for previous group completion), so, that means
    //        that we should process the 4-th and 5-th changes as the second group. Initial change direction is negative, hence, we
    //        process them from left to the right;
    //     3. Process the remaining change;
    if (changes.isEmpty()) {
      return;
    }
    
    int diff = 0;
    for (TextChangeImpl change : changes) {
      diff += change.getDiff();
    }
    if (length + diff > data.length) {
      throw new IllegalArgumentException(String.format(
        "Can't perform in-place changes merge. Reason: data array is not big enough to hold resulting text. Current size: %d, "
        + "minimum size: %d", data.length, length + diff
      ));
    }

    try {
      for (Context context = new Context(changes, data, length, length + diff); !context.isComplete();) {
        if (!context.startGroup()) {
          return;
        }
        context.endGroup();
      }
    }
    catch (RuntimeException e) {
      StringBuilder changesDescription = new StringBuilder();
      for (TextChangeImpl change : changes) {
        changesDescription.append(change.getText().length()).append(":").append(change.getStart()).append("-").append(change.getEnd())
          .append(",");
      }
      if (changesDescription.length() > 0) {
        changesDescription.setLength(changesDescription.length() - 1);
      } 
      LOG.error(String.format(
        "Invalid attempt to perform in-place document changes merge detected. Initial text length: %d, data array length: %d, "
        + "changes: [%s], changes diff: %d", length, data.length, changesDescription, diff
      ), e);
      char[] merged = mergeToCharArray(data, length, changes);
      System.arraycopy(merged, 0, data, 0, length + diff);
    }
  }
public void mergeInPlace(char @NotNull [] data, int length, @NotNull List<? extends TextChangeImpl> changes)