code: static String canonicalize(String input, int pos, int limit, String encodeSet,
                                 boolean alreadyEncoded, boolean strict, boolean plusIsSpace, boolean asciiOnly) {
          int codePoint;
          for (int i = pos; i < limit; i += Character.charCount(codePoint)) {
              codePoint = input.codePointAt(i);
              if (codePoint < 0x20
                      || codePoint == 0x7f
                      || codePoint >= 0x80 && asciiOnly
                      || encodeSet.indexOf(codePoint) != -1
                      || codePoint == '%' && (!alreadyEncoded || strict && !percentEncoded(input, i, limit))
                      || codePoint == '+' && plusIsSpace) {
                  // Slow path: the character at i requires encoding!
                  Buffer out = new Buffer();
                  out.writeUtf8(input, pos, i);
                  canonicalize(out, input, i, limit, encodeSet, alreadyEncoded, strict, plusIsSpace,
                          asciiOnly);
                  return out.readUtf8();
              }
          }
  
          // Fast path: no characters in [pos..limit) required encoding.
          return input.substring(pos, limit);
      }
docs: Returns a substring of {@code input} on the range {@code [pos..limit)} with the following
  transformations:
  <ul>
  <li>Tabs, newlines, form feeds and carriage returns are skipped.
  <li>In queries, ' ' is encoded to '+' and '+' is encoded to "%2B".
  <li>Characters in {@code encodeSet} are percent-encoded.
  <li>Control characters and non-ASCII characters are percent-encoded.
  <li>All other characters are copied without transformation.
  </ul>
  
  @param alreadyEncoded true to leave '%' as-is; false to convert it to '%25'.
  @param strict true to encode '%' if it is not the prefix of a valid percent encoding.
  @param plusIsSpace true to encode '+' as "%2B" if it is not already encoded.
  @param asciiOnly true to encode all non-ASCII codepoints.