Coverage Summary for Class: GhostBase64Kt (com.ghost.serialization.parser)

Class Class, % Method, % Branch, % Line, % Instruction, %
GhostBase64Kt 100% (1/1) 100% (2/2) 40.7% (22/54) 76.6% (49/64) 67.3% (302/449)


 package com.ghost.serialization.parser
 
 import com.ghost.serialization.parser.GhostJsonConstants as C
 
 /**
  * Decodes a standard or URL-safe Base64 string (with or without `=` padding) into raw bytes.
  *
  * Operates on an already-materialized Kotlin [String] (e.g. from `reader.nextString()`), so
  * it works from any reader flavor (streaming [GhostJsonReader], flat [GhostJsonFlatReader],
  * [GhostJsonStringReader]) — unlike a scratch-buffer-pooled decoder tied to a specific byte
  * buffer implementation.
  *
  * @throws IllegalArgumentException if [value] contains a non-alphabet, non-whitespace character.
  */
 fun decodeBase64String(value: String): ByteArray {
     val lut = C.BASE64_LUT
     val length = value.length
     val output = ByteArray(length)
     var outputPosition = 0
     val chunk = IntArray(4)
     var chunkIndex = 0
 
     for (index in 0 until length) {
         val c = value[index].code
         if (c <= C.SPACE_INT) continue
         chunk[chunkIndex++] = c
         if (chunkIndex == 4) {
             // Chars outside the LUT's range (any non-Latin-1 code unit) are never valid
             // base64 alphabet members — bounds-check rather than indexing lut[] directly,
             // since a raw index would throw ArrayIndexOutOfBoundsException instead of the
             // documented IllegalArgumentException.
             val value0 = if (chunk[0] < lut.size) lut[chunk[0]] else -1
             val value1 = if (chunk[1] < lut.size) lut[chunk[1]] else -1
             val value2 = if (chunk[2] < lut.size) lut[chunk[2]] else -1
             val value3 = if (chunk[3] < lut.size) lut[chunk[3]] else -1
             if (value0 < 0 || value1 < 0 || value2 == -1 || value3 == -1) {
                 throw IllegalArgumentException("Invalid base64 character")
             }
             output[outputPosition++] = ((value0 shl C.B64_SHIFT_2) or (value1 ushr C.B64_SHIFT_4)).toByte()
             if (value2 != -2) {
                 output[outputPosition++] = ((value1 shl C.B64_SHIFT_4) or (value2 ushr C.B64_SHIFT_2)).toByte()
                 if (value3 != -2) {
                     output[outputPosition++] = ((value2 shl C.B64_SHIFT_6) or value3).toByte()
                 }
             }
             chunkIndex = 0
         }
     }
 
     if (chunkIndex > 0) {
         while (chunkIndex < 4) {
             chunk[chunkIndex++] = '='.code
         }
         val value0 = if (chunk[0] < lut.size) lut[chunk[0]] else -1
         val value1 = if (chunk[1] < lut.size) lut[chunk[1]] else -1
         val value2 = if (chunk[2] < lut.size) lut[chunk[2]] else -1
         val value3 = if (chunk[3] < lut.size) lut[chunk[3]] else -1
         if (value0 >= 0 && value1 >= 0) {
             output[outputPosition++] = ((value0 shl C.B64_SHIFT_2) or (value1 ushr C.B64_SHIFT_4)).toByte()
             if (value2 != -2 && value2 >= 0) {
                 output[outputPosition++] = ((value1 shl C.B64_SHIFT_4) or (value2 ushr C.B64_SHIFT_2)).toByte()
             }
         }
     }
 
     return output.copyOf(outputPosition)
 }
 
 /**
  * Encodes [source] into a standard Base64 string with `=` padding (RFC 4648 §4).
  */
 fun encodeBase64String(source: ByteArray): String {
     if (source.isEmpty()) return ""
     val chars = C.BASE64_ALPHABET_BYTES
     val outputLength = ((source.size + C.B64_OFFSET_2) / C.B64_PAD_DIVISOR) * C.B64_PAD_MULTIPLIER
     val output = ByteArray(outputLength)
     var index = 0
     var outputIndex = 0
     val length = source.size
     val loopLimit = length - C.B64_OFFSET_2
     while (index < loopLimit) {
         val byte0 = source[index].toInt() and C.B64_BYTE_MASK
         val byte1 = source[index + C.B64_OFFSET_1].toInt() and C.B64_BYTE_MASK
         val byte2 = source[index + C.B64_OFFSET_2].toInt() and C.B64_BYTE_MASK
         output[outputIndex++] = chars[byte0 shr C.B64_SHIFT_2]
         output[outputIndex++] = chars[((byte0 and C.B64_MASK_2BITS) shl C.B64_SHIFT_4) or (byte1 shr C.B64_SHIFT_4)]
         output[outputIndex++] = chars[((byte1 and C.B64_MASK_4BITS) shl C.B64_SHIFT_2) or (byte2 shr C.B64_SHIFT_6)]
         output[outputIndex++] = chars[byte2 and C.B64_MASK_6BITS]
         index += C.B64_PAD_DIVISOR
     }
     if (index < length) {
         val byte0 = source[index].toInt() and C.B64_BYTE_MASK
         output[outputIndex++] = chars[byte0 shr C.B64_SHIFT_2]
         if (index == length - C.B64_OFFSET_1) {
             output[outputIndex++] = chars[(byte0 and C.B64_MASK_2BITS) shl C.B64_SHIFT_4]
             output[outputIndex++] = C.EQUALS_BYTE
             output[outputIndex++] = C.EQUALS_BYTE
         } else {
             val byte1 = source[index + C.B64_OFFSET_1].toInt() and C.B64_BYTE_MASK
             output[outputIndex++] = chars[((byte0 and C.B64_MASK_2BITS) shl C.B64_SHIFT_4) or (byte1 shr C.B64_SHIFT_4)]
             output[outputIndex++] = chars[(byte1 and C.B64_MASK_4BITS) shl C.B64_SHIFT_2]
             output[outputIndex++] = C.EQUALS_BYTE
         }
     }
     return output.decodeToString()
 }