/**
   * This method called when the string content is already in a char buffer, and need not be copied
   * for processing.
   */
  private void _writeString(char[] text, int offset, int len)
      throws IOException, JsonGenerationException {
    /* Let's just find longest spans of non-escapable
     * content, and for each see if it makes sense
     * to copy them, or write through
     */
    len += offset; // -> len marks the end from now on
    final int[] escCodes = CharTypes.getOutputEscapes();
    final int escLen = escCodes.length;
    while (offset < len) {
      int start = offset;

      while (true) {
        char c = text[offset];
        if (c < escLen && escCodes[c] != 0) {
          break;
        }
        if (++offset >= len) {
          break;
        }
      }

      // Short span? Better just copy it to buffer first:
      int newAmount = offset - start;
      if (newAmount < SHORT_WRITE) {
        // Note: let's reserve room for escaped char (up to 6 chars)
        if ((_outputTail + newAmount) > _outputEnd) {
          _flushBuffer();
        }
        if (newAmount > 0) {
          System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
          _outputTail += newAmount;
        }
      } else { // Nope: better just write through
        _flushBuffer();
        _writer.write(text, start, newAmount);
      }
      // Was this the end?
      if (offset >= len) { // yup
        break;
      }
      // Nope, need to escape the char.
      int escCode = escCodes[text[offset]];
      ++offset;
      int needLen = (escCode < 0) ? 6 : 2;
      if ((_outputTail + needLen) > _outputEnd) {
        _flushBuffer();
      }
      _appendSingleEscape(escCode, _outputBuffer, _outputTail);
      _outputTail += needLen;
    }
  }
  /**
   * Method called to output textual context which has been copied to the output buffer prior to
   * call. If any escaping is needed, it will also be handled by the method.
   *
   * <p>Note: when called, textual content to write is within output buffer, right after buffered
   * content (if any). That's why only length of that text is passed, as buffer and offset are
   * implied.
   */
  private final void _writeSegment(int end) throws IOException, JsonGenerationException {
    final int[] escCodes = CharTypes.getOutputEscapes();
    final int escLen = escCodes.length;

    int ptr = 0;

    output_loop:
    while (ptr < end) {
      // Fast loop for chars not needing escaping
      int start = ptr;
      while (true) {
        char c = _outputBuffer[ptr];
        if (c < escLen && escCodes[c] != 0) {
          break;
        }
        if (++ptr >= end) {
          break;
        }
      }

      // Ok, bumped into something that needs escaping.
      /* First things first: need to flush the buffer.
       * Inlined, as we don't want to lose tail pointer
       */
      int flushLen = (ptr - start);
      if (flushLen > 0) {
        _writer.write(_outputBuffer, start, flushLen);
        if (ptr >= end) {
          break output_loop;
        }
      }
      /* In any case, tail will be the new start, so hopefully
       * we have room now.
       */
      {
        int escCode = escCodes[_outputBuffer[ptr]];
        ++ptr;
        int needLen = (escCode < 0) ? 6 : 2;
        // If not, need to call separate method (note: buffer is empty now)
        if (needLen > _outputTail) {
          _writeSingleEscape(escCode);
        } else {
          // But if it fits, can just prepend to buffer
          ptr -= needLen;
          _appendSingleEscape(escCode, _outputBuffer, ptr);
        }
      }
    }
  }
  private void _writeString(String text) throws IOException, JsonGenerationException {
    /* One check first: if String won't fit in the buffer, let's
     * segment writes. No point in extending buffer to huge sizes
     * (like if someone wants to include multi-megabyte base64
     * encoded stuff or such)
     */
    int len = text.length();
    if (len > _outputEnd) { // Let's reserve space for entity at begin/end
      _writeLongString(text);
      return;
    }

    // Ok: we know String will fit in buffer ok
    // But do we need to flush first?
    if ((_outputTail + len) > _outputEnd) {
      _flushBuffer();
    }
    text.getChars(0, len, _outputBuffer, _outputTail);

    // And then we'll need to verify need for escaping etc:
    int end = _outputTail + len;
    final int[] escCodes = CharTypes.getOutputEscapes();
    final int escLen = escCodes.length;

    output_loop:
    while (_outputTail < end) {
      // Fast loop for chars not needing escaping
      escape_loop:
      while (true) {
        char c = _outputBuffer[_outputTail];
        if (c < escLen && escCodes[c] != 0) {
          break escape_loop;
        }
        if (++_outputTail >= end) {
          break output_loop;
        }
      }

      // Ok, bumped into something that needs escaping.
      /* First things first: need to flush the buffer.
       * Inlined, as we don't want to lose tail pointer
       */
      int flushLen = (_outputTail - _outputHead);
      if (flushLen > 0) {
        _writer.write(_outputBuffer, _outputHead, flushLen);
      }
      /* In any case, tail will be the new start, so hopefully
       * we have room now.
       */
      {
        int escCode = escCodes[_outputBuffer[_outputTail]];
        ++_outputTail;
        int needLen = (escCode < 0) ? 6 : 2;
        // If not, need to call separate method (note: buffer is empty now)
        if (needLen > _outputTail) {
          _outputHead = _outputTail;
          _writeSingleEscape(escCode);
        } else {
          // But if it fits, can just prepend to buffer
          int ptr = _outputTail - needLen;
          _outputHead = ptr;
          _appendSingleEscape(escCode, _outputBuffer, ptr);
        }
      }
    }
  }