
commonMain.kotlinx.serialization.internal.ElementMarker.kt Maven / Gradle / Ivy
/*
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.serialization.internal
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.CompositeDecoder
@OptIn(ExperimentalSerializationApi::class)
@CoreFriendModuleApi
public class ElementMarker(
private val descriptor: SerialDescriptor,
// Instead of inheritance and virtual function in order to keep cross-module internal modifier via suppresses
// Can be reworked via public + internal api if necessary
private val readIfAbsent: (SerialDescriptor, Int) -> Boolean
) {
/*
* Element decoding marks from given bytes.
* The element index is the same as the set bit position.
* Marks for the lowest 64 elements are always stored in a single Long value, higher elements stores in long array.
*/
private var lowerMarks: Long
private val highMarksArray: LongArray
private companion object {
private val EMPTY_HIGH_MARKS = LongArray(0)
}
init {
val elementsCount = descriptor.elementsCount
if (elementsCount <= Long.SIZE_BITS) {
lowerMarks = if (elementsCount == Long.SIZE_BITS) {
// number of bits in the mark is equal to the number of fields
0L
} else {
// (1 - elementsCount) bits are always 1 since there are no fields for them
-1L shl elementsCount
}
highMarksArray = EMPTY_HIGH_MARKS
} else {
lowerMarks = 0L
highMarksArray = prepareHighMarksArray(elementsCount)
}
}
public fun mark(index: Int) {
if (index < Long.SIZE_BITS) {
lowerMarks = lowerMarks or (1L shl index)
} else {
markHigh(index)
}
}
public fun nextUnmarkedIndex(): Int {
val elementsCount = descriptor.elementsCount
while (lowerMarks != -1L) {
val index = lowerMarks.inv().countTrailingZeroBits()
lowerMarks = lowerMarks or (1L shl index)
if (readIfAbsent(descriptor, index)) {
return index
}
}
if (elementsCount > Long.SIZE_BITS) {
return nextUnmarkedHighIndex()
}
return CompositeDecoder.DECODE_DONE
}
private fun prepareHighMarksArray(elementsCount: Int): LongArray {
// (elementsCount - 1) / Long.SIZE_BITS
// (elementsCount - 1) because only one Long value is needed to store 64 fields etc
val slotsCount = (elementsCount - 1) ushr 6
// elementsCount % Long.SIZE_BITS
val elementsInLastSlot = elementsCount and (Long.SIZE_BITS - 1)
val highMarks = LongArray(slotsCount)
// if (elementsCount % Long.SIZE_BITS) == 0 means that the fields occupy all bits in mark
if (elementsInLastSlot != 0) {
// all marks except the higher are always 0
highMarks[highMarks.lastIndex] = -1L shl elementsCount
}
return highMarks
}
private fun markHigh(index: Int) {
// (index / Long.SIZE_BITS) - 1
val slot = (index ushr 6) - 1
// index % Long.SIZE_BITS
val offsetInSlot = index and (Long.SIZE_BITS - 1)
highMarksArray[slot] = highMarksArray[slot] or (1L shl offsetInSlot)
}
private fun nextUnmarkedHighIndex(): Int {
for (slot in highMarksArray.indices) {
// (slot + 1) because first element in high marks has index 64
val slotOffset = (slot + 1) * Long.SIZE_BITS
// store in a variable so as not to frequently use the array
var slotMarks = highMarksArray[slot]
while (slotMarks != -1L) {
val indexInSlot = slotMarks.inv().countTrailingZeroBits()
slotMarks = slotMarks or (1L shl indexInSlot)
val index = slotOffset + indexInSlot
if (readIfAbsent(descriptor, index)) {
highMarksArray[slot] = slotMarks
return index
}
}
highMarksArray[slot] = slotMarks
}
return CompositeDecoder.DECODE_DONE
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy