<lambda>null1 package kotlinx.serialization.hocon.serializers
2
3 import com.typesafe.config.*
4 import java.math.BigInteger
5 import kotlinx.serialization.*
6 import kotlinx.serialization.descriptors.*
7 import kotlinx.serialization.encoding.*
8 import kotlinx.serialization.hocon.*
9
10 /**
11 * Serializer for [ConfigMemorySize].
12 * All possible Hocon size formats [https://github.com/lightbend/config/blob/main/HOCON.md#size-in-bytes-format] are accepted for decoding.
13 * During encoding, the serializer emits values using powers of two: byte, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB.
14 * Encoding uses the largest possible integer value.
15 * Example:
16 * 1024 byte -> 1 KiB;
17 * 1024 KiB -> 1 MiB;
18 * 1025 KiB -> 1025 KiB.
19 * Usage example:
20 * ```
21 * @Serializable
22 * data class ConfigMemory(
23 * @Serializable(ConfigMemorySizeSerializer::class)
24 * val size: ConfigMemorySize
25 * )
26 * val config = ConfigFactory.parseString("size = 1 MiB")
27 * val configMemory = Hocon.decodeFromConfig(ConfigMemory.serializer(), config)
28 * val newConfig = Hocon.encodeToConfig(ConfigMemory.serializer(), configMemory)
29 * ```
30 */
31 @ExperimentalSerializationApi
32 object ConfigMemorySizeSerializer : KSerializer<ConfigMemorySize> {
33
34 // For powers of two.
35 private val memoryUnitFormats = listOf("byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB")
36
37 override val descriptor: SerialDescriptor =
38 PrimitiveSerialDescriptor("hocon.com.typesafe.config.ConfigMemorySize", PrimitiveKind.STRING)
39
40 override fun deserialize(decoder: Decoder): ConfigMemorySize =
41 if (decoder is HoconDecoder) decoder.decodeConfigValue { conf, path -> conf.decodeMemorySize(path) }
42 else throwUnsupportedFormatException("ConfigMemorySizeSerializer")
43
44 override fun serialize(encoder: Encoder, value: ConfigMemorySize) {
45 if (encoder is HoconEncoder) {
46 // We determine that it is divisible by 1024 (2^10).
47 // And if it is divisible, then the number itself is shifted to the right by 10.
48 // And so on until we find one that is no longer divisible by 1024.
49 // ((n & ((1 << m) - 1)) == 0)
50 val andVal = BigInteger.valueOf(1023) // ((2^10) - 1) = 0x3ff = 1023
51 var bytes = value.toBytesBigInteger()
52 var unitIndex = 0
53 while (bytes.and(andVal) == BigInteger.ZERO) { // n & 0x3ff == 0
54 if (unitIndex < memoryUnitFormats.lastIndex) {
55 bytes = bytes.shiftRight(10)
56 unitIndex++
57 } else break
58 }
59 encoder.encodeString("$bytes ${memoryUnitFormats[unitIndex]}")
60 } else {
61 throwUnsupportedFormatException("ConfigMemorySizeSerializer")
62 }
63 }
64
65 private fun Config.decodeMemorySize(path: String): ConfigMemorySize = try {
66 getMemorySize(path)
67 } catch (e: ConfigException) {
68 throw SerializationException("Value at $path cannot be read as ConfigMemorySize because it is not a valid HOCON Size value", e)
69 }
70 }
71