/** Assigns {@code nextToken} based on the value of {@code nextValue}. */
 private JsonToken decodeLiteral() throws IOException {
   if (valuePos == -1) {
     // it was too long to fit in the buffer so it can only be a string
     return JsonToken.STRING;
   } else if (valueLength == 4
       && ('n' == buffer[valuePos] || 'N' == buffer[valuePos])
       && ('u' == buffer[valuePos + 1] || 'U' == buffer[valuePos + 1])
       && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2])
       && ('l' == buffer[valuePos + 3] || 'L' == buffer[valuePos + 3])) {
     value = "null";
     return JsonToken.NULL;
   } else if (valueLength == 4
       && ('t' == buffer[valuePos] || 'T' == buffer[valuePos])
       && ('r' == buffer[valuePos + 1] || 'R' == buffer[valuePos + 1])
       && ('u' == buffer[valuePos + 2] || 'U' == buffer[valuePos + 2])
       && ('e' == buffer[valuePos + 3] || 'E' == buffer[valuePos + 3])) {
     value = TRUE;
     return JsonToken.BOOLEAN;
   } else if (valueLength == 5
       && ('f' == buffer[valuePos] || 'F' == buffer[valuePos])
       && ('a' == buffer[valuePos + 1] || 'A' == buffer[valuePos + 1])
       && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2])
       && ('s' == buffer[valuePos + 3] || 'S' == buffer[valuePos + 3])
       && ('e' == buffer[valuePos + 4] || 'E' == buffer[valuePos + 4])) {
     value = FALSE;
     return JsonToken.BOOLEAN;
   } else {
     value = stringPool.get(buffer, valuePos, valueLength);
     return decodeNumber(buffer, valuePos, valueLength);
   }
 }
  /**
   * Unescapes the character identified by the character or characters that immediately follow a
   * backslash. The backslash '\' should have already been read. This supports both unicode escapes
   * "u000A" and two-character escapes "\n".
   *
   * @throws NumberFormatException if any unicode escape sequences are malformed.
   */
  private char readEscapeCharacter() throws IOException {
    if (pos == limit && !fillBuffer(1)) {
      throw syntaxError("Unterminated escape sequence");
    }

    char escaped = buffer[pos++];
    switch (escaped) {
      case 'u':
        if (pos + 4 > limit && !fillBuffer(4)) {
          throw syntaxError("Unterminated escape sequence");
        }
        String hex = stringPool.get(buffer, pos, 4);
        pos += 4;
        return (char) Integer.parseInt(hex, 16);

      case 't':
        return '\t';

      case 'b':
        return '\b';

      case 'n':
        return '\n';

      case 'r':
        return '\r';

      case 'f':
        return '\f';

      case '\'':
      case '"':
      case '\\':
      default:
        return escaped;
    }
  }
  /**
   * Returns the string up to but not including {@code quote}, unescaping any character escape
   * sequences encountered along the way. The opening quote should have already been read. This
   * consumes the closing quote, but does not include it in the returned string.
   *
   * @param quote either ' or ".
   * @throws NumberFormatException if any unicode escape sequences are malformed.
   */
  private String nextString(char quote) throws IOException {
    StringBuilder builder = null;
    do {
      /* the index of the first character not yet appended to the builder. */
      int start = pos;
      while (pos < limit) {
        int c = buffer[pos++];

        if (c == quote) {
          if (skipping) {
            return "skipped!";
          } else if (builder == null) {
            return stringPool.get(buffer, start, pos - start - 1);
          } else {
            builder.append(buffer, start, pos - start - 1);
            return builder.toString();
          }

        } else if (c == '\\') {
          if (builder == null) {
            builder = new StringBuilder();
          }
          builder.append(buffer, start, pos - start - 1);
          builder.append(readEscapeCharacter());
          start = pos;
        }
      }

      if (builder == null) {
        builder = new StringBuilder();
      }
      builder.append(buffer, start, pos - start);
    } while (fillBuffer(1));

    throw syntaxError("Unterminated string");
  }
  /**
   * Reads the value up to but not including any delimiter characters. This does not consume the
   * delimiter character.
   *
   * @param assignOffsetsOnly true for this method to only set the valuePos and valueLength fields
   *     and return a null result. This only works if the literal is short; a string is returned
   *     otherwise.
   */
  private String nextLiteral(boolean assignOffsetsOnly) throws IOException {
    StringBuilder builder = null;
    valuePos = -1;
    valueLength = 0;
    int i = 0;

    findNonLiteralCharacter:
    while (true) {
      for (; pos + i < limit; i++) {
        switch (buffer[pos + i]) {
          case '/':
          case '\\':
          case ';':
          case '#':
          case '=':
            checkLenient(); // fall-through
          case '{':
          case '}':
          case '[':
          case ']':
          case ':':
          case ',':
          case ' ':
          case '\t':
          case '\f':
          case '\r':
          case '\n':
            break findNonLiteralCharacter;
        }
      }

      /*
       * Attempt to load the entire literal into the buffer at once. If
       * we run out of input, add a non-literal character at the end so
       * that decoding doesn't need to do bounds checks.
       */
      if (i < buffer.length) {
        if (fillBuffer(i + 1)) {
          continue;
        } else {
          buffer[limit] = '\0';
          break;
        }
      }

      // use a StringBuilder when the value is too long. It must be an unquoted string.
      if (builder == null) {
        builder = new StringBuilder();
      }
      builder.append(buffer, pos, i);
      valueLength += i;
      pos += i;
      i = 0;
      if (!fillBuffer(1)) {
        break;
      }
    }

    String result;
    if (assignOffsetsOnly && builder == null) {
      valuePos = pos;
      result = null;
    } else if (skipping) {
      result = "skipped!";
    } else if (builder == null) {
      result = stringPool.get(buffer, pos, i);
    } else {
      builder.append(buffer, pos, i);
      result = builder.toString();
    }
    valueLength += i;
    pos += i;
    return result;
  }