public int hashString(CharSequence input, int start, int end) {
    int h1 = seed;
    int i = start;
    int len = 0;

    // This loop optimizes for pure ASCII.
    while (i + 4 <= end) {
      char c0 = input.charAt(i);
      char c1 = input.charAt(i + 1);
      char c2 = input.charAt(i + 2);
      char c3 = input.charAt(i + 3);
      if (c0 < 0x80 && c1 < 0x80 && c2 < 0x80 && c3 < 0x80) {
        int k1 = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
        k1 = mixK1(k1);
        h1 = mixH1(h1, k1);
        i += 4;
        len += 4;
      }
      else {
        break;
      }
    }

    long buffer = 0;
    int shift = 0;
    for (; i < end; i++) {
      char c = input.charAt(i);
      if (c < 0x80) {
        buffer |= (long)c << shift;
        shift += 8;
        len++;
      }
      else if (c < 0x800) {
        buffer |= charToTwoUtf8Bytes(c) << shift;
        shift += 16;
        len += 2;
      }
      else if (c < Character.MIN_SURROGATE || c > Character.MAX_SURROGATE) {
        buffer |= charToThreeUtf8Bytes(c) << shift;
        shift += 24;
        len += 3;
      }
      else {
        int codePoint = Character.codePointAt(input, i);
        if (codePoint == c) {
          // not a valid code point; let the JDK handle invalid Unicode
          byte[] bytes = input.toString().getBytes(StandardCharsets.UTF_8);
          return hashBytes(bytes, 0, bytes.length);
        }
        i++;
        buffer |= codePointToFourUtf8Bytes(codePoint) << shift;
        len += 4;
      }

      if (shift >= 32) {
        int k1 = mixK1((int)buffer);
        h1 = mixH1(h1, k1);
        buffer >>>= 32;
        shift -= 32;
      }
    }

    int k1 = mixK1((int)buffer);
    h1 ^= k1;
    return fMix(h1, len);
  }
public int hashString(CharSequence input, int start, int end)