Coverage Summary for Class: GhostJsonStringWriter (com.ghost.serialization.writer)

Class Class, % Method, % Branch, % Line, % Instruction, %
GhostJsonStringWriter 100% (1/1) 57.9% (33/57) 59% (95/161) 60% (225/375) 60.6% (876/1446)


 @file:OptIn(InternalGhostApi::class)
 
 package com.ghost.serialization.writer
 
 import com.ghost.serialization.InternalGhostApi
 import com.ghost.serialization.acquireScratchBuffer
 import com.ghost.serialization.exception.GhostJsonException
 import com.ghost.serialization.parser.GhostJsonConstants.ASCII_LIMIT
 import com.ghost.serialization.parser.GhostJsonConstants.BACKSLASH_INT
 import com.ghost.serialization.parser.GhostJsonConstants.BITMASK_INDEX_MASK
 import com.ghost.serialization.parser.GhostJsonConstants.BITMASK_SHIFT
 import com.ghost.serialization.parser.GhostJsonConstants.BITMASK_UNIT
 import com.ghost.serialization.parser.GhostJsonConstants.COLON_INT
 import com.ghost.serialization.parser.GhostJsonConstants.CLOSE_ARR_INT
 import com.ghost.serialization.parser.GhostJsonConstants.CLOSE_OBJ_INT
 import com.ghost.serialization.parser.GhostJsonConstants.COMMA_INT
 import com.ghost.serialization.parser.GhostJsonConstants.DOUBLE_DIGIT_LUT_CHARS
 import com.ghost.serialization.parser.GhostJsonConstants.ERR_DEPTH_EXCEEDED
 import com.ghost.serialization.parser.GhostJsonConstants.ERR_NON_FINITE
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_MASKS
 import com.ghost.serialization.parser.GhostJsonConstants.HEX_CHARS_CHARS
 import com.ghost.serialization.parser.GhostJsonConstants.HEX_MASK
 import com.ghost.serialization.parser.GhostJsonConstants.HUNDRED_LONG
 import com.ghost.serialization.parser.GhostJsonConstants.LONG_SCRATCH_SIZE
 import com.ghost.serialization.parser.GhostJsonConstants.MAX_DEPTH
 import com.ghost.serialization.parser.GhostJsonConstants.MAX_SAFE_INTEGER_DOUBLE
 import com.ghost.serialization.parser.GhostJsonConstants.MIN_SAFE_INTEGER_DOUBLE
 import com.ghost.serialization.parser.GhostJsonConstants.OPEN_ARR_INT
 import com.ghost.serialization.parser.GhostJsonConstants.OPEN_OBJ_INT
 import com.ghost.serialization.parser.GhostJsonConstants.QUOTE_INT
 import com.ghost.serialization.parser.GhostJsonConstants.SHIFT_12
 import com.ghost.serialization.parser.GhostJsonConstants.SHIFT_4
 import com.ghost.serialization.parser.GhostJsonConstants.SHIFT_8
 import com.ghost.serialization.parser.GhostJsonConstants.STRING_QUOTE_PAIR_BYTES
 import com.ghost.serialization.parser.GhostJsonConstants.TEN_LONG
 import com.ghost.serialization.parser.GhostJsonConstants.WHOLE_NUMBER_CHECK
 import com.ghost.serialization.parser.GhostJsonConstants.WRITER_SCRATCH_SIZE
 import com.ghost.serialization.parser.GhostJsonConstants.MINUS_INT
 import com.ghost.serialization.parser.GhostJsonConstants.SPACE_INT
 import com.ghost.serialization.parser.GhostJsonConstants.ZERO_DOUBLE
 import com.ghost.serialization.parser.GhostJsonConstants.ZERO_INT
 import com.ghost.serialization.parser.GhostJsonConstants.BS_INT
 import com.ghost.serialization.parser.GhostJsonConstants.FF_INT
 import com.ghost.serialization.parser.GhostJsonConstants.LF_INT
 import com.ghost.serialization.parser.GhostJsonConstants.CR_INT
 import com.ghost.serialization.parser.GhostJsonConstants.TAB_INT
 import com.ghost.serialization.parser.GhostJsonConstants.MIN_SINGLE_DIGIT
 import com.ghost.serialization.parser.GhostJsonConstants.MAX_SINGLE_DIGIT
 import com.ghost.serialization.parser.GhostJsonConstants.MIN_SINGLE_DIGIT_NEG
 import com.ghost.serialization.parser.GhostJsonConstants.MAX_SINGLE_DIGIT_NEG
 import com.ghost.serialization.parser.GhostJsonConstants.MIN_SINGLE_DIGIT_L
 import com.ghost.serialization.parser.GhostJsonConstants.MAX_SINGLE_DIGIT_L
 import com.ghost.serialization.parser.GhostJsonConstants.MIN_SINGLE_DIGIT_NEG_L
 import com.ghost.serialization.parser.GhostJsonConstants.MAX_SINGLE_DIGIT_NEG_L
 import com.ghost.serialization.parser.GhostJsonConstants.MIN_INT_STR
 import com.ghost.serialization.parser.GhostJsonConstants.MIN_LONG_STR
 import com.ghost.serialization.parser.GhostJsonConstants.CHAR_QUOTE
 import com.ghost.serialization.parser.GhostJsonConstants.CHAR_BACKSLASH
 import com.ghost.serialization.parser.GhostJsonConstants.CHAR_U
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_QUOTE
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_BACKSLASH
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_BACKSPACE
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_FORM_FEED
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_NEWLINE
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_CARRIAGE_RETURN
 import com.ghost.serialization.parser.GhostJsonConstants.ESCAPE_TAB
 import com.ghost.serialization.parser.GhostJsonConstants.ESC_B_INT
 import com.ghost.serialization.parser.GhostJsonConstants.ESC_F_INT
 import com.ghost.serialization.parser.GhostJsonConstants.ESC_N_INT
 import com.ghost.serialization.parser.GhostJsonConstants.ESC_R_INT
 import com.ghost.serialization.parser.GhostJsonConstants.ESC_T_INT
 import com.ghost.serialization.parser.GhostJsonConstants.RESULT_NONE
 import okio.ByteString
 
 @Suppress("SameParameterValue", "NOTHING_TO_INLINE")
 class GhostJsonStringWriter @InternalGhostApi constructor(
     @InternalGhostApi val buffer: FlatCharArrayWriter
 ) {
 
     @PublishedApi
     internal var needsComma: Boolean = false
 
     private var depth: Int = 0
 
     internal var scratch: CharArray? = null
 
     internal fun acquireScratch(): CharArray {
         val currentScratch = scratch
         if (currentScratch != null) return currentScratch
 
         val newScratch = CharArray(WRITER_SCRATCH_SIZE)
         scratch = newScratch
         return newScratch
     }
 
     @Suppress("unused")
     @InternalGhostApi
     fun release() {
         scratch = null
         needsComma = false
         depth = 0
     }
 
     @InternalGhostApi
     fun reset() {
         needsComma = false
         depth = 0
     }
 
     @InternalGhostApi
     @Suppress("EmptyFunctionBlock")
     fun flush() {
         /* No Ops */
     }
 
     // ── Structural ────────────────────────────────────────────────────────────
 
     fun beginObject(): GhostJsonStringWriter {
         val currentDepth = depth
         if (currentDepth >= MAX_DEPTH) {
             throwDepthError()
         }
         appendSeparator()
         buffer.writeChar(OPEN_OBJ_INT)
         needsComma = false
         depth = currentDepth + 1
         return this
     }
 
     fun endObject(): GhostJsonStringWriter {
         buffer.writeChar(CLOSE_OBJ_INT)
         needsComma = true
         depth--
         return this
     }
 
     fun beginArray(): GhostJsonStringWriter {
         val currentDepth = depth
         if (currentDepth >= MAX_DEPTH) {
             throwDepthError()
         }
         appendSeparator()
         buffer.writeChar(OPEN_ARR_INT)
         needsComma = false
         depth = currentDepth + 1
         return this
     }
 
     fun endArray(): GhostJsonStringWriter {
         buffer.writeChar(CLOSE_ARR_INT)
         needsComma = true
         depth--
         return this
     }
 
     fun name(key: String): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeChar(QUOTE_INT)
         writeEscaped(key)
         buffer.write2Chars(QUOTE_INT, COLON_INT)
         needsComma = false
         return this
     }
 
     fun name(key: ByteString): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeAscii(key)
         needsComma = false
         return this
     }
 
     @InternalGhostApi
     fun writeNameRaw(header: ByteString): GhostJsonStringWriter {
         return name(header)
     }
 
     @InternalGhostApi
     fun writeField(header: ByteString, value: Int): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeAscii(header)
         writeIntValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: ByteString, value: Long): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeAscii(header)
         writeLongValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: ByteString, value: String): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeAscii(header)
         writeStringValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: ByteString, value: Boolean): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeAscii(header)
         writeBooleanValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: ByteString, value: Double): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeAscii(header)
         writeDoubleValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: ByteString, value: Float): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeAscii(header)
         writeFloatValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeNameRaw(header: String): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeString(header)
         needsComma = false
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: String, value: Int): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeString(header)
         writeIntValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: String, value: Long): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeString(header)
         writeLongValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: String, value: String): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeString(header)
         writeStringValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: String, value: Boolean): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeString(header)
         writeBooleanValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: String, value: Double): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeString(header)
         writeDoubleValueRaw(value)
         needsComma = true
         return this
     }
 
     @InternalGhostApi
     fun writeField(header: String, value: Float): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeString(header)
         writeFloatValueRaw(value)
         needsComma = true
         return this
     }
 
     // ── value() public API ────────────────────────────────────────────────────
 
     fun value(text: String): GhostJsonStringWriter {
         appendSeparator()
         writeStringValueRaw(text)
         needsComma = true
         return this
     }
 
     fun value(number: Int): GhostJsonStringWriter {
         appendSeparator()
         writeIntValueRaw(number)
         needsComma = true
         return this
     }
 
     fun value(number: Long): GhostJsonStringWriter {
         appendSeparator()
         writeLongValueRaw(number)
         needsComma = true
         return this
     }
 
     fun value(number: Double): GhostJsonStringWriter {
         appendSeparator()
         writeDoubleValueRaw(number)
         needsComma = true
         return this
     }
 
     fun value(number: Float): GhostJsonStringWriter {
         appendSeparator()
         writeFloatValueRaw(number)
         needsComma = true
         return this
     }
 
     fun value(value: Boolean): GhostJsonStringWriter {
         appendSeparator()
         if (value) {
             buffer.writeTrue()
         } else {
             buffer.writeFalse()
         }
         needsComma = true
         return this
     }
 
     /**
      * Writes a single [Char] as a JSON string without allocating an intermediate [String].
      */
     fun value(char: Char): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeChar(QUOTE_INT)
         buffer.writeChar(char.code)
         buffer.writeChar(QUOTE_INT)
         needsComma = true
         return this
     }
 
     fun nullValue(): GhostJsonStringWriter {
         appendSeparator()
         buffer.writeNull()
         needsComma = true
         return this
     }
 
     /**
      * Writes raw JSON bytes directly into the stream without quoting or escaping.
      * The bytes are decoded from UTF-8 to the internal char buffer.
      */
     fun rawValue(bytes: ByteArray): GhostJsonStringWriter {
         appendSeparator()
         buffer.appendUtf8(bytes, 0, bytes.size)
         needsComma = true
         return this
     }
 
     /** Writes a slice of raw JSON bytes after decoding the UTF-8 range. */
     fun rawValue(bytes: ByteArray, offset: Int, length: Int): GhostJsonStringWriter {
         appendSeparator()
         buffer.appendUtf8(bytes, offset, length)
         needsComma = true
         return this
     }
 
     /** Writes [raw] using its storage slice. */
     fun rawValue(raw: com.ghost.serialization.types.RawJson): GhostJsonStringWriter =
         rawValue(raw.storage, raw.storageOffset, raw.storageLength)
 
     @InternalGhostApi
     fun writeBooleanValueRaw(value: Boolean) {
         if (value) {
             buffer.writeTrue()
         } else {
             buffer.writeFalse()
         }
     }
 
     @InternalGhostApi
     fun writeIntValueRaw(value: Int) {
         if (value in MIN_SINGLE_DIGIT..MAX_SINGLE_DIGIT) {
             buffer.writeChar(ZERO_INT + value)
             return
         }
         if (value in MIN_SINGLE_DIGIT_NEG..MAX_SINGLE_DIGIT_NEG) {
             buffer.write2Chars(MINUS_INT, ZERO_INT - value)
             return
         }
         if (value == Int.MIN_VALUE) {
             buffer.writeString(MIN_INT_STR)
             return
         }
         writeLongValueRawInternal(value.toLong())
     }
 
     @InternalGhostApi
     fun writeLongValueRaw(value: Long) {
         if (value in MIN_SINGLE_DIGIT_L..MAX_SINGLE_DIGIT_L) {
             val intVal = value.toInt()
             buffer.writeChar(ZERO_INT + intVal)
             return
         }
         if (value in MIN_SINGLE_DIGIT_NEG_L..MAX_SINGLE_DIGIT_NEG_L) {
             val intVal = value.toInt()
             buffer.write2Chars(MINUS_INT, ZERO_INT - intVal)
             return
         }
         if (value == Int.MIN_VALUE.toLong()) {
             buffer.writeString(MIN_INT_STR)
             return
         }
         if (value == Long.MIN_VALUE) {
             buffer.writeString(MIN_LONG_STR)
             return
         }
         writeLongValueRawInternal(value)
     }
 
     private fun writeLongValueRawInternal(value: Long) {
         val scratchBuf = scratch ?: acquireScratch()
         val scratchEnd = LONG_SCRATCH_SIZE
         var scratchIndex = scratchEnd
         var localValue = value
         val isNegative = localValue < 0
         if (isNegative) {
             if (localValue == Long.MIN_VALUE) {
                 buffer.writeString(MIN_LONG_STR)
                 return
             }
             localValue = -localValue
         }
 
         while (localValue >= HUNDRED_LONG) {
             val lutIndex = (localValue % HUNDRED_LONG).toInt() * 2
             scratchBuf[--scratchIndex] = DOUBLE_DIGIT_LUT_CHARS[lutIndex + 1]
             scratchBuf[--scratchIndex] = DOUBLE_DIGIT_LUT_CHARS[lutIndex]
             localValue /= HUNDRED_LONG
         }
 
         if (localValue < TEN_LONG) {
             scratchBuf[--scratchIndex] = (ZERO_INT + localValue.toInt()).toChar()
         } else {
             val lutIndex = localValue.toInt() * 2
             scratchBuf[--scratchIndex] = DOUBLE_DIGIT_LUT_CHARS[lutIndex + 1]
             scratchBuf[--scratchIndex] = DOUBLE_DIGIT_LUT_CHARS[lutIndex]
         }
 
         if (isNegative) {
             scratchBuf[--scratchIndex] = MINUS_INT.toChar()
         }
 
         buffer.write(scratchBuf, scratchIndex, scratchEnd - scratchIndex)
     }
 
     @InternalGhostApi
     fun writeDoubleValueRaw(number: Double) {
         if (number in MIN_SAFE_INTEGER_DOUBLE..MAX_SAFE_INTEGER_DOUBLE &&
             number % WHOLE_NUMBER_CHECK == ZERO_DOUBLE &&
             !(number == 0.0 && number.toRawBits() < 0)
         ) {
             writeLongValueRawInternal(number.toLong())
             buffer.writeDotZero()
             return
         }
 
         val scratchBuf = acquireScratch()
         // Format double to a byte scratch then write it to flat char writer
         val byteScratch = acquireScratchBuffer(WRITER_SCRATCH_SIZE)
         try {
             val bytesWrittenLength = GhostDoubleFormatter.writeDoubleDirect(
                 value = number,
                 scratch = byteScratch,
                 offset = 0,
                 fallback = { fallbackNum ->
                     if (!fallbackNum.isFinite()) {
                         throw GhostJsonException(ERR_NON_FINITE, 0, 0)
                     }
                     buffer.writeString(fallbackNum.toString())
                     -1
                 }
             )
 
             if (bytesWrittenLength > 0) {
                 // Write formatted bytes as chars
                 for (i in 0 until bytesWrittenLength) {
                     scratchBuf[i] = byteScratch[i].toInt().toChar()
                 }
                 buffer.write(scratchBuf, 0, bytesWrittenLength)
             }
         } finally {
             com.ghost.serialization.releaseScratchBuffer(byteScratch)
         }
     }
 
     fun writeFloatValueRaw(number: Float) {
         val doubleVal = number.toDouble()
         if (doubleVal in MIN_SAFE_INTEGER_DOUBLE..MAX_SAFE_INTEGER_DOUBLE &&
             doubleVal % WHOLE_NUMBER_CHECK == ZERO_DOUBLE &&
             !(number == 0.0f && number.toRawBits() < 0)
         ) {
             writeLongValueRawInternal(doubleVal.toLong())
             buffer.writeDotZero()
             return
         }
 
         val scratchBuf = acquireScratch()
         val byteScratch = acquireScratchBuffer(WRITER_SCRATCH_SIZE)
         try {
             val bytesWrittenLength = GhostDoubleFormatter.writeFloatDirect(
                 value = number,
                 scratch = byteScratch,
                 offset = 0,
                 fallback = { fallbackNum ->
                     if (!fallbackNum.isFinite()) {
                         throw GhostJsonException(ERR_NON_FINITE, 0, 0)
                     }
                     buffer.writeString(fallbackNum.toString())
                     -1
                 }
             )
 
             if (bytesWrittenLength > 0) {
                 for (i in 0 until bytesWrittenLength) {
                     scratchBuf[i] = byteScratch[i].toInt().toChar()
                 }
                 buffer.write(scratchBuf, 0, bytesWrittenLength)
             }
         } finally {
             com.ghost.serialization.releaseScratchBuffer(byteScratch)
         }
     }
 
     @Suppress("unused")
     @InternalGhostApi
     fun writeNullValueRaw() {
         buffer.writeNull()
     }
 
     @PublishedApi
     @Suppress("NOTHING_TO_INLINE")
     internal inline fun appendSeparator() {
         if (needsComma) {
             buffer.writeChar(COMMA_INT)
             needsComma = false
         }
     }
 
     @InternalGhostApi
     fun writeStringValueRaw(value: String) {
         val length = value.length
         if (length == 0) {
             buffer.write2Chars(QUOTE_INT, QUOTE_INT)
             return
         }
 
         val localAsciiLimit = ASCII_LIMIT
         val localEscapeMasks = ESCAPE_MASKS
         val localShift = BITMASK_SHIFT
         val localIndexMask = BITMASK_INDEX_MASK
         val localUnit = BITMASK_UNIT
         val resultNone = RESULT_NONE
 
         var index = 0
         while (index < length) {
             val code = value[index].code
             if (!isPlainAscii(
                     code,
                     localAsciiLimit,
                     localEscapeMasks,
                     localShift,
                     localIndexMask,
                     localUnit,
                     resultNone
                 )
             ) {
                 writeStringValueRawSlow(value, length, index)
                 return
             }
             index++
         }
         buffer.writeQuotedAscii(value, length)
     }
 
     private fun writeStringValueRawSlow(value: String, length: Int, breakIndex: Int) {
         val scratchBuf = acquireScratch()
         if (breakIndex == 0 && length + STRING_QUOTE_PAIR_BYTES <= scratchBuf.size) {
             scratchBuf[0] = CHAR_QUOTE
             writeEscapedIntoScratch(value, length, scratchBuf)
             return
         }
         buffer.writeChar(QUOTE_INT)
         if (breakIndex > 0) {
             buffer.writeString(value, 0, breakIndex)
         }
         writeEscaped(value, start = breakIndex)
         buffer.writeChar(QUOTE_INT)
     }
 
     private inline fun getEscapeSecondChar(code: Int): Int {
         return when (code) {
             QUOTE_INT -> QUOTE_INT
             BACKSLASH_INT -> BACKSLASH_INT
             BS_INT -> ESC_B_INT
             FF_INT -> ESC_F_INT
             LF_INT -> ESC_N_INT
             CR_INT -> ESC_R_INT
             TAB_INT -> ESC_T_INT
             else -> 0
         }
     }
 
     private fun writeEscaped(text: String, start: Int = 0) {
         val scratchBuf = acquireScratch()
         val length = text.length
         val remaining = length - start
         if (remaining <= 0) {
             return
         }
 
         val scratchSize = scratchBuf.size
         val localAsciiLimit = ASCII_LIMIT
         val localEscapeMasks = ESCAPE_MASKS
         val localShift = BITMASK_SHIFT
         val localIndexMask = BITMASK_INDEX_MASK
         val localUnit = BITMASK_UNIT
         val resultNone = RESULT_NONE
 
         if (remaining <= scratchSize) {
             var scratchIndex = 0
             var charIndex = start
             while (charIndex < length) {
                 val charCode = text[charIndex].code
 
                 if (isPlainAscii(
                         charCode,
                         localAsciiLimit,
                         localEscapeMasks,
                         localShift,
                         localIndexMask,
                         localUnit,
                         resultNone
                     )
                 ) {
                     scratchBuf[scratchIndex++] = charCode.toChar()
                     charIndex++
                     continue
                 }
 
                 if (scratchIndex > 0) {
                     buffer.write(scratchBuf, 0, scratchIndex)
                     scratchIndex = 0
                 }
 
                 if (charCode < localAsciiLimit) {
                     val esc = getEscapeSecondChar(charCode)
                     if (esc != 0) {
                         buffer.write2Chars(BACKSLASH_INT, esc)
                     } else {
                         writeUnicodeEscape(charCode, scratchBuf)
                     }
                 } else {
                     buffer.writeChar(charCode)
                 }
                 charIndex++
             }
             if (scratchIndex > 0) {
                 buffer.write(scratchBuf, 0, scratchIndex)
             }
             return
         }
 
         var scratchIndex = 0
         var charIndex = start
 
         while (charIndex < length) {
             val charCode = text[charIndex].code
 
             if (isPlainAscii(
                     charCode,
                     localAsciiLimit,
                     localEscapeMasks,
                     localShift,
                     localIndexMask,
                     localUnit,
                     resultNone
                 )
             ) {
                 scratchBuf[scratchIndex++] = charCode.toChar()
                 if (scratchIndex == scratchSize) {
                     buffer.write(scratchBuf, 0, scratchIndex)
                     scratchIndex = 0
                 }
                 charIndex++
                 continue
             }
 
             if (scratchIndex > 0) {
                 buffer.write(scratchBuf, 0, scratchIndex)
                 scratchIndex = 0
             }
 
             if (charCode < localAsciiLimit) {
                 val esc = getEscapeSecondChar(charCode)
                 if (esc != 0) {
                     buffer.write2Chars(BACKSLASH_INT, esc)
                 } else {
                     writeUnicodeEscape(charCode, scratchBuf)
                 }
             } else {
                 buffer.writeChar(charCode)
             }
             charIndex++
         }
 
         if (scratchIndex > 0) {
             buffer.write(scratchBuf, 0, scratchIndex)
         }
     }
 
     private fun writeEscapedIntoScratch(text: String, length: Int, scratchBuf: CharArray) {
         var scratchIndex = 1
         var charIndex = 0
         val localAsciiLimit = ASCII_LIMIT
         val localEscapeMasks = ESCAPE_MASKS
         val localShift = BITMASK_SHIFT
         val localIndexMask = BITMASK_INDEX_MASK
         val localUnit = BITMASK_UNIT
         val resultNone = RESULT_NONE
 
         while (charIndex < length) {
             val charCode = text[charIndex].code
 
             if (isPlainAscii(
                     charCode,
                     localAsciiLimit,
                     localEscapeMasks,
                     localShift,
                     localIndexMask,
                     localUnit,
                     resultNone
                 )
             ) {
                 scratchBuf[scratchIndex++] = charCode.toChar()
                 charIndex++
                 continue
             }
 
             if (scratchIndex > 0) {
                 buffer.write(scratchBuf, 0, scratchIndex)
                 scratchIndex = 0
             }
 
             if (charCode < localAsciiLimit) {
                 val esc = getEscapeSecondChar(charCode)
                 if (esc != 0) {
                     buffer.write2Chars(BACKSLASH_INT, esc)
                 } else {
                     writeUnicodeEscape(charCode, scratchBuf)
                 }
             } else {
                 buffer.writeChar(charCode)
             }
             charIndex++
         }
 
         if (scratchIndex + 1 > scratchBuf.size) {
             if (scratchIndex > 0) {
                 buffer.write(scratchBuf, 0, scratchIndex)
             }
             buffer.writeChar(QUOTE_INT)
         } else {
             scratchBuf[scratchIndex++] = CHAR_QUOTE
             buffer.write(scratchBuf, 0, scratchIndex)
         }
     }
 
     private fun throwDepthError() {
         throw GhostJsonException("$ERR_DEPTH_EXCEEDED (${MAX_DEPTH})", 0, 0)
     }
 
     private fun writeUnicodeEscape(code: Int, scratchBuf: CharArray) {
         val hexChars = HEX_CHARS_CHARS
 
         scratchBuf[0] = CHAR_BACKSLASH
         scratchBuf[1] = CHAR_U
         scratchBuf[2] = hexChars[(code shr SHIFT_12) and HEX_MASK]
         scratchBuf[3] = hexChars[(code shr SHIFT_8) and HEX_MASK]
         scratchBuf[4] = hexChars[(code shr SHIFT_4) and HEX_MASK]
         scratchBuf[5] = hexChars[code and HEX_MASK]
 
         buffer.write(scratchBuf, 0, 6)
     }
 
     private inline fun isPlainAscii(
         charCode: Int,
         asciiLimit: Int,
         escapeMasks: LongArray,
         shift: Int,
         indexMask: Int,
         unit: Long,
         resultNone: Long
     ): Boolean {
         return charCode < asciiLimit &&
                 ((escapeMasks[charCode shr shift] shr (charCode and indexMask)) and unit) == resultNone
     }
 }