public void init(@NotNull final LogicalPosition logical, final @NotNull List<CacheEntry> cache) {
    reset();

    myTargetLogical = logical;
    int start = 0;
    int end = cache.size() - 1;

    // We inline binary search here because profiling indicates that it becomes bottleneck to use
    // Collections.binarySearch().
    while (start <= end) {
      int i = (end + start) >>> 1;
      CacheEntry cacheEntry = cache.get(i);

      // There is a possible case that single logical line is represented on multiple visual lines
      // due to soft wraps processing.
      // Hence, we check for bot logical line and logical columns during searching 'anchor' cache
      // entry.

      if (cacheEntry.endLogicalLine < logical.line
          || (cacheEntry.endLogicalLine == logical.line
              && myStorage.getSoftWrap(cacheEntry.endOffset) != null
              && cacheEntry.endLogicalColumn <= logical.column)) {
        start = i + 1;
        continue;
      }
      if (cacheEntry.startLogicalLine > logical.line
          || (cacheEntry.startLogicalLine == logical.line
              && cacheEntry.startLogicalColumn > logical.column)) {
        end = i - 1;
        continue;
      }

      // There is a possible case that currently found cache entry corresponds to soft-wrapped line
      // and soft wrap occurred
      // at target logical column. We need to return cache entry for the next visual line then
      // (because single logical column
      // is shared for 'before soft wrap' and 'after soft wrap' positions and we want to use the one
      // that points to
      // 'after soft wrap' position).
      if (cacheEntry.endLogicalLine == logical.line
          && cacheEntry.endLogicalColumn == logical.column
          && i < cache.size() - 1) {
        CacheEntry nextLineCacheEntry = cache.get(i + 1);
        if (nextLineCacheEntry.startLogicalLine == logical.line
            && nextLineCacheEntry.startLogicalColumn == logical.column) {
          setInitialPosition(nextLineCacheEntry.buildStartLinePosition());
          return;
        }
      }
      setInitialPosition(cacheEntry.buildStartLinePosition());
      return;
    }

    throw new IllegalStateException(
        String.format(
            "Can't map logical position (%s) to visual position. Reason: no cached information information about target visual "
                + "line is found. Registered entries: %s",
            logical, cache));
  }