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