Coverage Summary for Class: RawJsonValueScanner (com.ghost.serialization.types)

Class Method, % Branch, % Line, % Instruction, %
RawJsonValueScanner 100% (17/17) 77.7% (220/283) 97.4% (150/154) 92.9% (973/1047)
RawJsonValueScanner$WhenMappings
Total 100% (17/17) 77.7% (220/283) 97.4% (150/154) 92.9% (973/1047)


 package com.ghost.serialization.types
 
 /**
  * Zero-allocation scanners for [RawJson] JSON value classification and scalar coercion.
  *
  * Operates directly on [RawJson.storage] / [RawJson.storageOffset] / [RawJson.storageLength]
  * without materializing [RawJson.bytes] unless a [String] result is required.
  */
 internal object RawJsonValueScanner {
 
     private const val QUOTE = '"'.code
     private const val OPEN_OBJ = '{'.code
     private const val OPEN_ARR = '['.code
     private const val MINUS = '-'.code
     private const val PLUS = '+'.code
     private const val DOT = '.'.code
     private const val ZERO = '0'.code
     private const val NINE = '9'.code
     private const val EXP_LOWER = 'e'.code
     private const val EXP_UPPER = 'E'.code
     private const val BACKSLASH = '\\'.code
 
     private const val TRUE_LEN = 4
     private const val FALSE_LEN = 5
     private const val NULL_LEN = 4
 
     private const val NULL_CHAR = 'n'.code
     private const val TRUE_CHAR = 't'.code
     private const val FALSE_CHAR = 'f'.code
     private const val ONE = '1'.code
     private const val CHAR_R = 'r'.code
     private const val CHAR_U = 'u'.code
     private const val CHAR_E = 'e'.code
     private const val CHAR_A = 'a'.code
     private const val CHAR_L = 'l'.code
     private const val CHAR_S = 's'.code
 
     fun kind(raw: RawJson): RawJsonKind {
         if (raw.storageLength <= 0) return RawJsonKind.INVALID
         return when (val first = raw.byteAt(0)) {
             OPEN_OBJ -> RawJsonKind.OBJECT
             OPEN_ARR -> RawJsonKind.ARRAY
             QUOTE -> RawJsonKind.STRING
             NULL_CHAR -> if (raw.matchesNullLiteral()) RawJsonKind.NULL else RawJsonKind.INVALID
             TRUE_CHAR -> if (raw.matchesTrueLiteral()) RawJsonKind.BOOLEAN else RawJsonKind.INVALID
             FALSE_CHAR -> if (raw.matchesFalseLiteral()) RawJsonKind.BOOLEAN else RawJsonKind.INVALID
             MINUS, ZERO -> if (raw.isJsonNumberToken()) RawJsonKind.NUMBER else RawJsonKind.INVALID
             in ONE..NINE -> if (raw.isJsonNumberToken()) RawJsonKind.NUMBER else RawJsonKind.INVALID
             else -> RawJsonKind.INVALID
         }
     }
 
     fun isJsonNull(raw: RawJson): Boolean =
         raw.storageLength == NULL_LEN && raw.matchesNullLiteral()
 
     fun asBooleanOrNull(raw: RawJson): Boolean? = when {
         raw.storageLength == TRUE_LEN && raw.matchesTrueLiteral() -> true
         raw.storageLength == FALSE_LEN && raw.matchesFalseLiteral() -> false
         else -> null
     }
 
     fun asIntOrNull(raw: RawJson): Int? {
         val longValue = raw.parseIntegerOrNull() ?: return null
         if (longValue < Int.MIN_VALUE || longValue > Int.MAX_VALUE) return null
         return longValue.toInt()
     }
 
     fun asLongOrNull(raw: RawJson): Long? = raw.parseIntegerOrNull()
 
     fun asDoubleOrNull(raw: RawJson): Double? {
         raw.parseIntegerOrNull()?.let { return it.toDouble() }
         if (!raw.isJsonNumberToken()) return null
         return raw.decodeToString().toDoubleOrNull()
     }
 
     fun asStringOrNull(raw: RawJson): String? {
         if (raw.storageLength < 2 || raw.byteAt(0) != QUOTE) return null
         val contentStart = raw.storageOffset + 1
         val contentEnd = raw.storageOffset + raw.storageLength - 1
         if (contentEnd < contentStart) return ""
         for (index in contentStart until contentEnd) {
             if (raw.storage[index] == BACKSLASH.toByte()) {
                 return raw.decodeJsonStringWithEscapes(contentStart, contentEnd)
             }
         }
         return raw.storage.decodeToString(contentStart, contentEnd)
     }
 
     fun asDisplayString(raw: RawJson): String {
         val classified = kind(raw)
         return when (classified) {
             RawJsonKind.STRING -> asStringOrNull(raw) ?: raw.decodeToString()
             RawJsonKind.NUMBER,
             RawJsonKind.BOOLEAN,
             RawJsonKind.NULL -> raw.decodeToString()
             RawJsonKind.OBJECT,
             RawJsonKind.ARRAY,
             RawJsonKind.INVALID -> raw.decodeToString()
         }
     }
 
     private fun RawJson.byteAt(relativeIndex: Int): Int =
         storage[storageOffset + relativeIndex].toInt() and 0xFF
 
     private fun RawJson.matchesTrueLiteral(): Boolean =
         storageLength == TRUE_LEN &&
             storage[storageOffset].toInt() == TRUE_CHAR &&
             storage[storageOffset + 1].toInt() == CHAR_R &&
             storage[storageOffset + 2].toInt() == CHAR_U &&
             storage[storageOffset + 3].toInt() == CHAR_E
 
     private fun RawJson.matchesFalseLiteral(): Boolean =
         storageLength == FALSE_LEN &&
             storage[storageOffset].toInt() == FALSE_CHAR &&
             storage[storageOffset + 1].toInt() == CHAR_A &&
             storage[storageOffset + 2].toInt() == CHAR_L &&
             storage[storageOffset + 3].toInt() == CHAR_S &&
             storage[storageOffset + 4].toInt() == CHAR_E
 
     private fun RawJson.matchesNullLiteral(): Boolean =
         storageLength == NULL_LEN &&
             storage[storageOffset].toInt() == NULL_CHAR &&
             storage[storageOffset + 1].toInt() == CHAR_U &&
             storage[storageOffset + 2].toInt() == CHAR_L &&
             storage[storageOffset + 3].toInt() == CHAR_L
 
     private fun RawJson.isJsonNumberToken(): Boolean {
         var index = 0
         if (byteAt(index) == MINUS) {
             index++
             if (index >= storageLength) return false
         }
         if (byteAt(index) == ZERO) {
             index++
         } else {
             if (byteAt(index) !in ONE..NINE) return false
             index++
             while (index < storageLength && byteAt(index) in ZERO..NINE) {
                 index++
             }
         }
         if (index < storageLength && byteAt(index) == DOT) {
             index++
             if (index >= storageLength || byteAt(index) !in ZERO..NINE) return false
             while (index < storageLength && byteAt(index) in ZERO..NINE) {
                 index++
             }
         }
         if (index < storageLength && (byteAt(index) == EXP_LOWER || byteAt(index) == EXP_UPPER)) {
             index++
             if (index < storageLength && (byteAt(index) == MINUS || byteAt(index) == PLUS)) {
                 index++
             }
             if (index >= storageLength || byteAt(index) !in ZERO..NINE) return false
             while (index < storageLength && byteAt(index) in ZERO..NINE) {
                 index++
             }
         }
         return index == storageLength
     }
 
     /**
      * Single-pass integer parse: rejects fraction/exponent without rescanning the token.
      */
     private fun RawJson.parseIntegerOrNull(): Long? {
         if (storageLength <= 0) return null
         var index = 0
         var negative = false
         when (byteAt(index)) {
             MINUS -> {
                 negative = true
                 index++
             }
             ZERO, in ONE..NINE -> Unit
             else -> return null
         }
         if (index >= storageLength) return null
 
         var value = 0L
         val limit = if (negative) Long.MIN_VALUE else -Long.MAX_VALUE
 
         if (byteAt(index) == ZERO) {
             index++
             if (index < storageLength) {
                 when (byteAt(index)) {
                     DOT, EXP_LOWER, EXP_UPPER -> return null
                     in ZERO..NINE -> return null
                 }
             }
             return if (negative) 0L else 0L
         }
 
         while (index < storageLength) {
             when (val byte = byteAt(index)) {
                 in ZERO..NINE -> {
                     val digit = byte - ZERO
                     if (value < limit / 10) return null
                     value *= 10
                     val next = value - digit
                     if (next > value) return null
                     value = next
                     index++
                 }
                 DOT, EXP_LOWER, EXP_UPPER -> return null
                 else -> return null
             }
         }
         // A positive token whose magnitude is exactly -Long.MIN_VALUE accumulates to
         // Long.MIN_VALUE here; negating it would silently wrap back to Long.MIN_VALUE
         // instead of overflowing, so treat it as out of range.
         if (!negative && value == Long.MIN_VALUE) return null
         return if (negative) value else -value
     }
 
     private fun RawJson.decodeJsonStringWithEscapes(contentStart: Int, contentEnd: Int): String {
         val estimated = contentEnd - contentStart
         val builder = StringBuilder(estimated)
         var index = contentStart
         while (index < contentEnd) {
             val byte = storage[index++].toInt() and 0xFF
             if (byte == BACKSLASH && index < contentEnd) {
                 when (val escaped = storage[index++].toInt() and 0xFF) {
                     QUOTE -> builder.append('"')
                     BACKSLASH -> builder.append('\\')
                     'b'.code -> builder.append('\b')
                     'f'.code -> builder.append('\u000C')
                     'n'.code -> builder.append('\n')
                     'r'.code -> builder.append('\r')
                     't'.code -> builder.append('\t')
                     'u'.code -> {
                         if (index + 3 >= contentEnd) return builder.toString()
                         val hex = readHex4(index)
                         index += 4
                         if (hex in 0xD800..0xDBFF && index + 5 < contentEnd &&
                             storage[index] == BACKSLASH.toByte() &&
                             storage[index + 1] == 'u'.code.toByte()
                         ) {
                             val low = readHex4(index + 2)
                             index += 6
                             appendCodePoint(builder, 0x10000 + ((hex - 0xD800) shl 10) + (low - 0xDC00))
                         } else {
                             appendCodePoint(builder, hex)
                         }
                     }
                     else -> builder.append(escaped.toChar())
                 }
             } else {
                 builder.append(byte.toChar())
             }
         }
         return builder.toString()
     }
 
     private fun appendCodePoint(builder: StringBuilder, codePoint: Int) {
         if (codePoint <= 0xFFFF) {
             builder.append(codePoint.toChar())
             return
         }
         val offset = codePoint - 0x10000
         builder.append((0xD800 + (offset shr 10)).toChar())
         builder.append((0xDC00 + (offset and 0x3FF)).toChar())
     }
 
     private fun RawJson.readHex4(start: Int): Int {
         var value = 0
         var shift = 0
         while (shift < 4) {
             val nibble = hexValue(storage[start + shift].toInt() and 0xFF)
             if (nibble < 0) return 0xFFFD
             value = (value shl 4) or nibble
             shift++
         }
         return value
     }
 
     private fun hexValue(byte: Int): Int = when (byte) {
         in ZERO..NINE -> byte - ZERO
         in 'a'.code..'f'.code -> byte - 'a'.code + 10
         in 'A'.code..'F'.code -> byte - 'A'.code + 10
         else -> -1
     }
 }