Coverage Summary for Class: FlatCharArrayWriter (com.ghost.serialization.writer)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| FlatCharArrayWriter |
100%
(1/1)
|
81%
(17/21)
|
73.3%
(22/30)
|
78.7%
(111/141)
|
78%
(520/667)
|
package com.ghost.serialization.writer
import com.ghost.serialization.InternalGhostApi
import com.ghost.serialization.parser.GhostJsonConstants.BUFFER_SCALE_FACTOR
import com.ghost.serialization.parser.GhostJsonConstants.ERR_CAPACITY_OVERFLOW_PREFIX
import com.ghost.serialization.parser.GhostJsonConstants.INITIAL_WRITE_BUFFER_SIZE
import com.ghost.serialization.parser.GhostJsonConstants.STRING_QUOTE_PAIR_BYTES
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_QUOTE
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_T
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_R
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_U
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_E
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_F
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_A
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_L
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_S
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_N
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_DOT
import com.ghost.serialization.parser.GhostJsonConstants.CHAR_ZERO
import com.ghost.serialization.parser.GhostJsonConstants.CAPACITY_GROWTH_SHIFT
import com.ghost.serialization.parser.GhostJsonConstants as C
import com.ghost.serialization.parser.GhostHeuristics.maxWarmCharWriteBufferCapacity
import okio.ByteString
/**
* A growing flat-array character buffer used as the in-memory output target for
* [GhostJsonStringWriter]. Concrete final class with no interface or superclass
* to allow direct JIT/AOT inlining.
*/
@InternalGhostApi
class FlatCharArrayWriter(private val initialCapacity: Int = INITIAL_WRITE_BUFFER_SIZE) {
var array: CharArray = CharArray(initialCapacity)
private set
var size: Int = 0
private set
private fun ensureCapacity(extraChars: Int) {
val requiredCapacity = size + extraChars
if (requiredCapacity < 0) {
throw IllegalStateException(ERR_CAPACITY_OVERFLOW_PREFIX + "size=$size, extraChars=$extraChars")
}
if (requiredCapacity > array.size) {
var newCapacity = array.size
if (newCapacity == 0) {
newCapacity = INITIAL_WRITE_BUFFER_SIZE
}
while (newCapacity < requiredCapacity) {
val nextCapacity = newCapacity + (newCapacity shr CAPACITY_GROWTH_SHIFT)
if (nextCapacity < newCapacity) {
newCapacity = Int.MAX_VALUE
break
}
newCapacity = nextCapacity
}
if (newCapacity < requiredCapacity) {
newCapacity = requiredCapacity
}
array = array.copyOf(newCapacity)
}
}
/** Appends a single character. */
fun writeChar(charAsInt: Int) {
val currentSize = size
val backingArray = array
if (currentSize < backingArray.size) {
backingArray[currentSize] = charAsInt.toChar()
size = currentSize + 1
} else {
growAndWrite(charAsInt)
}
}
private fun growAndWrite(charAsInt: Int) {
ensureCapacity(1)
array[size++] = charAsInt.toChar()
}
/** Appends exactly two characters. */
fun write2Chars(firstChar: Int, secondChar: Int) {
val currentSize = size
val backingArray = array
if (currentSize + 1 < backingArray.size) {
backingArray[currentSize] = firstChar.toChar()
backingArray[currentSize + 1] = secondChar.toChar()
size = currentSize + 2
} else {
ensureCapacity(2)
val updatedArray = array
updatedArray[currentSize] = firstChar.toChar()
updatedArray[currentSize + 1] = secondChar.toChar()
size = currentSize + 2
}
}
/** Writes a plain-ASCII string directly with minimal checks. */
fun writeQuotedAscii(text: String, length: Int) {
ensureCapacity(length + STRING_QUOTE_PAIR_BYTES)
val backingArray = array
val writeIndex = size
backingArray[writeIndex] = CHAR_QUOTE
text.copyRangeToCharArray(backingArray, writeIndex + 1, 0, length)
backingArray[writeIndex + 1 + length] = CHAR_QUOTE
size = writeIndex + length + STRING_QUOTE_PAIR_BYTES
}
fun write(chars: CharArray) {
ensureCapacity(chars.size)
chars.copyInto(array, size)
size += chars.size
}
fun write(chars: CharArray, offset: Int, length: Int) {
ensureCapacity(length)
chars.copyInto(
array,
size,
offset,
offset + length
)
size += length
}
fun writeString(text: String) {
writeString(text, 0, text.length)
}
fun writeString(text: String, beginIndex: Int, endIndex: Int) {
val length = endIndex - beginIndex
ensureCapacity(length)
text.copyRangeToCharArray(array, size, beginIndex, endIndex)
size += length
}
/** Decodes a UTF-8 byte range directly into the char buffer without an intermediate [String]. */
fun appendUtf8(bytes: ByteArray, offset: Int, length: Int) {
if (length <= 0) {
return
}
ensureCapacity(length)
val backingArray = array
var writeIndex = size
var readIndex = offset
val end = offset + length
while (readIndex < end) {
val byte = bytes[readIndex].toInt() and 0xFF
when {
byte <= C.UTF8_1BYTE_MAX -> {
backingArray[writeIndex++] = byte.toChar()
readIndex++
}
(byte shr 5) == 0x6 -> {
val b2 = bytes[readIndex + 1].toInt() and 0xFF
val codePoint = ((byte and 0x1F) shl 6) or (b2 and 0x3F)
backingArray[writeIndex++] = codePoint.toChar()
readIndex += 2
}
(byte shr 4) == 0xE -> {
val b2 = bytes[readIndex + 1].toInt() and 0xFF
val b3 = bytes[readIndex + 2].toInt() and 0xFF
val codePoint = ((byte and 0x0F) shl 12) or ((b2 and 0x3F) shl 6) or (b3 and 0x3F)
backingArray[writeIndex++] = codePoint.toChar()
readIndex += 3
}
else -> {
val b2 = bytes[readIndex + 1].toInt() and 0xFF
val b3 = bytes[readIndex + 2].toInt() and 0xFF
val b4 = bytes[readIndex + 3].toInt() and 0xFF
val codePoint = ((byte and 0x07) shl 18) or
((b2 and 0x3F) shl 12) or
((b3 and 0x3F) shl 6) or
(b4 and 0x3F)
if (writeIndex + 1 >= backingArray.size) {
size = writeIndex
ensureCapacity(2)
}
val updatedArray = array
val high = ((codePoint - 0x10000) shr 10) + 0xD800
val low = ((codePoint - 0x10000) and 0x3FF) + 0xDC00
updatedArray[writeIndex++] = high.toChar()
updatedArray[writeIndex++] = low.toChar()
readIndex += 4
}
}
}
size = writeIndex
}
/** Writes a ByteString interpreting its bytes directly as ASCII chars. */
fun writeAscii(byteString: ByteString) {
val str = byteString.utf8()
val length = str.length
ensureCapacity(length)
str.copyRangeToCharArray(array, size, 0, length)
size += length
}
/** Writes the literal "true". */
fun writeTrue() {
ensureCapacity(4)
val backingArray = array
var writeIndex = size
backingArray[writeIndex++] = CHAR_T
backingArray[writeIndex++] = CHAR_R
backingArray[writeIndex++] = CHAR_U
backingArray[writeIndex++] = CHAR_E
size = writeIndex
}
/** Writes the literal "false". */
fun writeFalse() {
ensureCapacity(5)
val backingArray = array
var writeIndex = size
backingArray[writeIndex++] = CHAR_F
backingArray[writeIndex++] = CHAR_A
backingArray[writeIndex++] = CHAR_L
backingArray[writeIndex++] = CHAR_S
backingArray[writeIndex++] = CHAR_E
size = writeIndex
}
/** Writes the literal "null". */
fun writeNull() {
ensureCapacity(4)
val backingArray = array
var writeIndex = size
backingArray[writeIndex++] = CHAR_N
backingArray[writeIndex++] = CHAR_U
backingArray[writeIndex++] = CHAR_L
backingArray[writeIndex++] = CHAR_L
size = writeIndex
}
/** Writes the literal ".0". */
fun writeDotZero() {
ensureCapacity(2)
val backingArray = array
var writeIndex = size
backingArray[writeIndex++] = CHAR_DOT
backingArray[writeIndex++] = CHAR_ZERO
size = writeIndex
}
fun reset() {
size = 0
contractCapacity()
}
private fun contractCapacity() {
if (array.size > maxWarmCharWriteBufferCapacity) {
array = CharArray(initialCapacity)
}
}
fun toCharArray(): CharArray = array.copyOf(size)
override fun toString(): String {
return array.concatToString(0, size)
}
}