All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jetbrains.exodus.env.BitmapImpl.kt Maven / Gradle / Ivy

There is a newer version: 9.8.0.76914
Show newest version
/**
 * Copyright 2010 - 2022 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.exodus.env

import jetbrains.exodus.ArrayByteIterable
import jetbrains.exodus.ByteIterable
import jetbrains.exodus.bindings.LongBinding
import jetbrains.exodus.bindings.LongBinding.compressedEntryToLong
import jetbrains.exodus.core.dataStructures.hash.LongHashMap

private typealias Bit = Long

open class BitmapImpl(open val store: StoreImpl) : Bitmap {

    override fun getEnvironment() = store.environment

    override fun get(txn: Transaction, bit: Bit): Boolean {
        val key = bit.ensureNonNegative().key
        val bitmapEntry = store.get(txn, LongBinding.longToCompressedEntry(key)) ?: return false
        val mask = 1L shl bit.index
        return bitmapEntry.asLong and mask != 0L
    }

    override fun set(txn: Transaction, bit: Bit, value: Boolean): Boolean {
        val key = bit.ensureNonNegative().key
        val keyEntry = LongBinding.longToCompressedEntry(key)
        val mask = 1L shl bit.index
        val bitmap = store.get(txn, keyEntry)?.asLong ?: 0L
        val prevValue = bitmap and mask != 0L
        if (prevValue == value) return false
        (bitmap xor mask).let {
            if (it == 0L) {
                store.delete(txn, keyEntry)
            } else {
                store.put(txn, keyEntry, it.asEntry)
            }
        }
        return true
    }

    override fun clear(txn: Transaction, bit: Bit): Boolean = this.set(txn, bit, false)

    override fun iterator(txn: Transaction): BitmapIterator = BitmapIterator(txn, store)

    override fun reverseIterator(txn: Transaction): BitmapIterator = BitmapIterator(txn, store, -1)

    override fun getFirst(txn: Transaction): Long {
        return iterator(txn).use {
            if (it.hasNext()) it.next() else -1L
        }
    }

    override fun getLast(txn: Transaction): Long {
        return reverseIterator(txn).use {
            if (it.hasNext()) it.next() else -1L
        }
    }

    override fun count(txn: Transaction): Long {
        store.openCursor(txn).use { cursor ->
            var count = 0L
            cursor.forEach {
                count += value.countBits
            }
            return count
        }
    }

    override fun count(txn: Transaction, firstBit: Bit, lastBit: Bit): Long {
        if (firstBit > lastBit) throw IllegalArgumentException("firstBit > lastBit")
        if (firstBit == lastBit) {
            return if (get(txn, firstBit)) 1L else 0L
        }
        store.openCursor(txn).use { cursor ->
            val firstKey = firstBit.ensureNonNegative().key
            val lastKey = lastBit.ensureNonNegative().key
            val keyEntry = LongBinding.longToCompressedEntry(firstKey)
            val valueEntry = cursor.getSearchKeyRange(keyEntry) ?: return 0L
            var count = 0L
            val key = compressedEntryToLong(cursor.key)
            if (key in (firstKey + 1) until lastKey) {
                count += valueEntry.countBits
            } else {
                val bits = valueEntry.asLong
                val lowBit = if (key == firstKey) firstBit.index else 0
                val highBit = if (key == lastKey) lastBit.index else Long.SIZE_BITS - 1
                for (i in lowBit..highBit) {
                    if (bits and (1L shl i) != 0L) {
                        ++count
                    }
                }
            }
            cursor.forEach {
                val currentKey = compressedEntryToLong(this.key)
                if (currentKey < lastKey) {
                    count += value.countBits
                } else {
                    if (currentKey == lastKey) {
                        val bits = value.asLong
                        for (i in 0..lastBit.index) {
                            if (bits and (1L shl i) != 0L) {
                                ++count
                            }
                        }
                    }
                    return count
                }
            }
            return count
        }
    }

    companion object {

        private const val ALL_ONES = -1L
        private val SINGLE_ZEROS = LongHashMap()
        private val SINGLE_ONES = LongHashMap()

        init {
            var bit = 1L
            for (i in 0..63) {
                SINGLE_ONES[bit] = i
                SINGLE_ZEROS[ALL_ONES xor bit] = i
                bit = bit shl 1
            }
        }

        internal val Long.asEntry: ByteIterable
            get() {
                if (this == ALL_ONES) {
                    return ArrayByteIterable(byteArrayOf(0))
                }
                SINGLE_ONES[this]?.let { bit ->
                    return ArrayByteIterable(byteArrayOf((bit + 1).toByte()))
                }
                SINGLE_ZEROS[this]?.let { bit ->
                    return ArrayByteIterable(byteArrayOf((bit + Long.SIZE_BITS + 1).toByte()))
                }
                return LongBinding.longToEntry(this)
            }

        internal val ByteIterable.asLong: Long
            get() {
                if (this.length != 1) {
                    return LongBinding.entryToLong(this)
                }
                this.iterator().next().unsigned.let { tag ->
                    return when {
                        tag == 0 -> ALL_ONES
                        tag < Long.SIZE_BITS + 1 -> 1L shl (tag - 1)
                        else -> ALL_ONES xor (1L shl (tag - Long.SIZE_BITS - 1))
                    }
                }
            }

        private val ByteIterable.countBits: Int
            get() {
                this.iterator().let {
                    if (length == 1) {
                        return it.next().unsigned.let { tag ->
                            when {
                                tag == 0 -> 64
                                tag < Long.SIZE_BITS + 1 -> 1
                                else -> 63
                            }
                        }
                    }
                    var size = 0
                    size += (it.next().unsigned xor 0x80).countOneBits()
                    while (it.hasNext()) {
                        size += it.next().unsigned.countOneBits()
                    }
                    return size
                }
            }
    }
}

internal val Bit.key: Bit get() = this shr 6

internal val Bit.index: Int get() = (this and 63).toInt()

private val Byte.unsigned: Int get() = this.toInt() and 0xff

private fun Bit.ensureNonNegative() =
    this.also { if (it < 0L) throw IllegalArgumentException("Bit number should be non-negative") }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy