jetbrains.exodus.log.BlockSet.kt Maven / Gradle / Ivy
/**
* 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.log
import jetbrains.exodus.core.dataStructures.hash.LongIterator
import jetbrains.exodus.core.dataStructures.persistent.PersistentBitTreeLongMap
import jetbrains.exodus.core.dataStructures.persistent.PersistentLongMap
import jetbrains.exodus.io.Block
// block key is aligned block address, i.e. block address divided by blockSize
sealed class BlockSet(val blockSize: Long, val set: PersistentLongMap) {
protected abstract val current: PersistentLongMap.ImmutableMap
fun size() = current.size()
val isEmpty get() = current.isEmpty
val minimum: Long? get() = current.iterator().let { if (it.hasNext()) it.next().key.keyToAddress else null }
val maximum: Long? get() = current.reverseIterator().let { if (it.hasNext()) it.next().key.keyToAddress else null }
/**
* Array of blocks' addresses in reverse order: the newer blocks first
*/
val array: LongArray
get() = getFiles(reversed = true)
@JvmOverloads
fun getFiles(reversed: Boolean = false): LongArray {
val current = current
val result = LongArray(current.size())
val it = if (reversed) {
current.reverseIterator()
} else {
current.iterator()
}
for (i in 0 until result.size) {
result[i] = it.next().key.keyToAddress
}
return result
}
fun contains(blockAddress: Long) = current.containsKey(blockAddress.addressToKey)
fun getBlock(blockAddress: Long): Block = current.get(blockAddress.addressToKey) ?: EmptyBlock(blockAddress)
// if address is inside of a block, the block containing it must be included as well if present
fun getFilesFrom(blockAddress: Long = 0L): LongIterator = object : LongIterator {
val it = if (blockAddress == 0L) current.iterator() else current.tailEntryIterator(blockAddress.addressToKey)
override fun next() = nextLong()
override fun hasNext() = it.hasNext()
override fun nextLong() = it.next().key.keyToAddress
override fun remove() = throw UnsupportedOperationException()
}
fun beginWrite() = Mutable(blockSize, set.clone)
protected val Long.keyToAddress: Long get() = this * blockSize
protected val Long.addressToKey: Long get() = this / blockSize
class Immutable @JvmOverloads constructor(
blockSize: Long,
map: PersistentLongMap = PersistentBitTreeLongMap()
) : BlockSet(blockSize, map) {
private val immutable: PersistentLongMap.ImmutableMap = map.beginRead()
public override val current: PersistentLongMap.ImmutableMap
get() = immutable
}
class Mutable(blockSize: Long, map: PersistentLongMap) : BlockSet(blockSize, map) {
private val mutable: PersistentLongMap.MutableMap = set.beginWrite()
override val current: PersistentLongMap.ImmutableMap
get() = mutable
fun clear() = mutable.clear()
fun add(blockAddress: Long, block: Block) = mutable.put(blockAddress.addressToKey, block)
fun remove(blockAddress: Long) = mutable.remove(blockAddress.addressToKey) != null
fun endWrite(): Immutable {
if (!mutable.endWrite()) {
throw IllegalStateException("File set can't be updated")
}
return Immutable(blockSize, set.clone)
}
}
private inner class EmptyBlock(private val address: Long) : Block {
override fun getAddress() = address
override fun length() = 0L
override fun read(output: ByteArray, position: Long, offset: Int, count: Int) = 0
override fun refresh() = this
}
}