swaydb.core.data.KeyValue.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2020 Simer JS Plaha ([email protected] - @simerplaha)
*
* This file is a part of SwayDB.
*
* SwayDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SwayDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with SwayDB. If not, see .
*
* Additional permission under the GNU Affero GPL version 3 section 7:
* If you modify this Program or any covered work, only by linking or combining
* it with separate works, the licensors of this Program grant you additional
* permission to convey the resulting work.
*/
package swaydb.core.data
import swaydb.IO
import swaydb.core.map.serializer.RangeValueSerializer.OptionRangeValueSerializer
import swaydb.core.map.serializer.{RangeValueSerializer, ValueSerializer}
import swaydb.core.segment.KeyMatcher
import swaydb.core.segment.format.a.block.reader.UnblockedReader
import swaydb.core.segment.format.a.block.values.ValuesBlock
import swaydb.core.util.Bytes
import swaydb.data.MaxKey
import swaydb.data.cache.{Cache, CacheNoIO}
import swaydb.data.order.KeyOrder
import swaydb.data.slice.{Slice, SliceOption}
import swaydb.data.util.{SomeOrNone, SomeOrNoneCovariant, TupleOrNone}
import scala.concurrent.duration.{Deadline, FiniteDuration}
private[core] sealed trait KeyValueOption {
def getUnsafe: KeyValue
def toOptional: Option[KeyValue] =
this match {
case optional: MemoryOption =>
optional.toOptionS
case optional: PersistentOption =>
optional.toOptionS
}
}
private[core] sealed trait KeyValue {
def key: Slice[Byte]
def indexEntryDeadline: Option[Deadline]
def toMemory: Memory
def keyLength =
key.size
}
private[core] object KeyValue {
sealed trait Null extends KeyValueOption
/**
* Key-values that can be added to [[swaydb.core.actor.MemorySweeper]].
*
* These key-values can remain in memory depending on the cacheSize and are dropped or uncompressed on overflow.
*/
sealed trait CacheAble extends KeyValue {
def valueLength: Int
}
/**
* An API response type expected from a [[swaydb.core.map.Map]] or [[swaydb.core.segment.Segment]].
*/
sealed trait Fixed extends KeyValue {
def toFromValue(): Value.FromValue
def toRangeValue(): Value.RangeValue
def time: Time
}
sealed trait PutOption {
def getPut: KeyValue.Put
def isNoneS: Boolean
def isSome: Boolean =
!isNoneS
def toTuple: Option[(Slice[Byte], Option[Slice[Byte]])] =
if (isNoneS) {
None
} else {
val put = this.getPut
val value = put.getOrFetchValue
val key = put.key
Some((key, value.toOptionC))
}
def toTupleOrNone: TupleOrNone[Slice[Byte], SliceOption[Byte]] =
if (isNoneS) {
TupleOrNone.None
} else {
val put = this.getPut
TupleOrNone.Some(put.key, put.getOrFetchValue)
}
def toDeadlineOrNone: TupleOrNone[Slice[Byte], Option[Deadline]] =
if (isNoneS) {
TupleOrNone.None
} else {
val put = this.getPut
TupleOrNone.Some(put.key, put.deadline)
}
def toKeyValueDeadlineOrNone: TupleOrNone[(Slice[Byte], SliceOption[Byte]), Option[Deadline]] =
if (isNoneS) {
TupleOrNone.None
} else {
val put = this.getPut
TupleOrNone.Some((put.key, put.getOrFetchValue), put.deadline)
}
def getValue: Option[SliceOption[Byte]] =
if (isNoneS)
None
else
Some(this.getPut.getOrFetchValue)
def getKey: SliceOption[Byte] =
if (isNoneS)
Slice.Null
else
this.getPut.key
def toOptionPut: Option[KeyValue.Put] =
if (isNoneS)
None
else
Some(getPut)
def flatMap(put: KeyValue.Put => PutOption): PutOption =
if (isNoneS)
this
else
put(getPut)
def getOrElse(f: => PutOption): PutOption =
if (isSome)
this
else
f
def orElse(f: => PutOption): PutOption =
if (isNoneS)
f
else
this
def map[T](f: KeyValue.Put => T): Option[T] =
if (isNoneS)
None
else
Some(f(getPut))
def flatMapOption[T](f: KeyValue.Put => Option[T]): Option[T] =
if (isNoneS)
None
else
f(getPut)
def mapSliceOptional(f: KeyValue.Put => SliceOption[Byte]): SliceOption[Byte] =
if (isNoneS)
Slice.Null
else
f(getPut)
}
object Put {
final case object Null extends PutOption {
override def getPut: KeyValue.Put = throw new Exception("KeyValue.Put is of type Null")
override def isNoneS: Boolean = true
}
}
sealed trait Put extends KeyValue.Fixed with PutOption {
def valueLength: Int
def deadline: Option[Deadline]
def hasTimeLeft(): Boolean
def isOverdue(): Boolean = !hasTimeLeft()
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean
def getOrFetchValue: SliceOption[Byte]
def time: Time
def toFromValue(): Value.Put
def copyWithDeadlineAndTime(deadline: Option[Deadline], time: Time): KeyValue.Put
def copyWithTime(time: Time): KeyValue.Put
}
sealed trait Remove extends KeyValue.Fixed {
def deadline: Option[Deadline]
def hasTimeLeft(): Boolean
def isOverdue(): Boolean = !hasTimeLeft()
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean
def time: Time
def toFromValue(): Value.Remove
def toRemoveValue(): Value.Remove
def copyWithTime(time: Time): KeyValue.Remove
}
sealed trait Update extends KeyValue.Fixed {
def deadline: Option[Deadline]
def hasTimeLeft(): Boolean
def isOverdue(): Boolean = !hasTimeLeft()
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean
def time: Time
def getOrFetchValue: SliceOption[Byte]
def toFromValue(): Value.Update
def toPut(): KeyValue.Put
def toPut(deadline: Option[Deadline]): KeyValue.Put
def copyWithDeadlineAndTime(deadline: Option[Deadline], time: Time): KeyValue.Update
def copyWithDeadline(deadline: Option[Deadline]): KeyValue.Update
def copyWithTime(time: Time): KeyValue.Update
}
sealed trait Function extends KeyValue.Fixed {
def time: Time
def getOrFetchFunction: Slice[Byte]
def toFromValue(): Value.Function
def copyWithTime(time: Time): Function
}
sealed trait PendingApply extends KeyValue.Fixed {
def getOrFetchApplies: Slice[Value.Apply]
def toFromValue(): Value.PendingApply
def time: Time
def deadline: Option[Deadline]
}
object Range {
def contains(range: KeyValue.Range, key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]) =
keyOrder.gteq(key, range.fromKey) && keyOrder.lt(key, range.toKey) //key >= range.fromKey && key < range.toKey
def containsLower(range: KeyValue.Range, key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]) =
keyOrder.gt(key, range.fromKey) && keyOrder.lteq(key, range.toKey) //key > range.fromKey && key <= range.toKey
}
sealed trait Range extends KeyValue {
def fromKey: Slice[Byte]
def toKey: Slice[Byte]
def fetchFromValueUnsafe: Value.FromValueOption
def fetchRangeValueUnsafe: Value.RangeValue
def fetchFromAndRangeValueUnsafe: (Value.FromValueOption, Value.RangeValue)
def fetchFromOrElseRangeValueUnsafe: Value.FromValue = {
val (fromValue, rangeValue) = fetchFromAndRangeValueUnsafe
fromValue getOrElseS rangeValue
}
}
}
private[swaydb] sealed trait MemoryOption extends SomeOrNone[MemoryOption, Memory] with KeyValueOption {
override def noneS: MemoryOption = Memory.Null
override def getUnsafe: KeyValue =
getS
}
private[swaydb] sealed trait Memory extends KeyValue with MemoryOption {
def id: Byte
def isRange: Boolean
def isRemoveRangeMayBe: Boolean
def isPut: Boolean
def persistentTime: Time
def mergedKey: Slice[Byte]
def value: SliceOption[Byte]
def deadline: Option[Deadline]
def unslice(): Memory
override def isNoneS: Boolean =
false
override def getS: Memory =
this
}
private[swaydb] object Memory {
val emtpySeq = Seq.empty[Memory]
final case object Null extends MemoryOption with KeyValue.Null {
override val isNoneS: Boolean = true
override def getS: Memory =
throw new Exception("Is Null")
}
implicit class MemoryIterableImplicits(keyValues: Slice[Memory]) {
@inline final def maxKey(): MaxKey[Slice[Byte]] =
keyValues.last match {
case range: Memory.Range =>
MaxKey.Range(range.fromKey, range.toKey)
case fixed: Memory.Fixed =>
MaxKey.Fixed(fixed.key)
}
@inline final def minKey: Slice[Byte] =
keyValues.head.key
}
sealed trait Fixed extends Memory with KeyValue.Fixed {
def isRange: Boolean = false
def unslice(): Memory.Fixed
}
object Put {
final val id = 1.toByte
}
//if value is empty byte slice, return None instead of empty Slice.We do not store empty byte arrays.
def compressibleValue(keyValue: Memory): SliceOption[Byte] =
if (keyValue.value.existsC(_.isEmpty))
Slice.Null
else
keyValue.value
def hasSameValue(left: Memory, right: Memory): Boolean =
(left, right) match {
//Remove
case (left: Memory.Remove, right: Memory.Remove) => true
case (left: Memory.Remove, right: Memory.Put) => right.value.isNoneC
case (left: Memory.Remove, right: Memory.Update) => right.value.isNoneC
case (left: Memory.Remove, right: Memory.Function) => false
case (left: Memory.Remove, right: Memory.PendingApply) => false
case (left: Memory.Remove, right: Memory.Range) => false
//Put
case (left: Memory.Put, right: Memory.Remove) => left.value.isNoneC
case (left: Memory.Put, right: Memory.Put) => left.value == right.value
case (left: Memory.Put, right: Memory.Update) => left.value == right.value
case (left: Memory.Put, right: Memory.Function) => left.value containsC right.function
case (left: Memory.Put, right: Memory.PendingApply) => false
case (left: Memory.Put, right: Memory.Range) => false
//Update
case (left: Memory.Update, right: Memory.Remove) => left.value.isNoneC
case (left: Memory.Update, right: Memory.Put) => left.value == right.value
case (left: Memory.Update, right: Memory.Update) => left.value == right.value
case (left: Memory.Update, right: Memory.Function) => left.value containsC right.function
case (left: Memory.Update, right: Memory.PendingApply) => false
case (left: Memory.Update, right: Memory.Range) => false
//Function
case (left: Memory.Function, right: Memory.Remove) => false
case (left: Memory.Function, right: Memory.Put) => right.value containsC left.function
case (left: Memory.Function, right: Memory.Update) => right.value containsC left.function
case (left: Memory.Function, right: Memory.Function) => left.function == right.function
case (left: Memory.Function, right: Memory.PendingApply) => false
case (left: Memory.Function, right: Memory.Range) => false
//PendingApply
case (left: Memory.PendingApply, right: Memory.Remove) => false
case (left: Memory.PendingApply, right: Memory.Put) => false
case (left: Memory.PendingApply, right: Memory.Update) => false
case (left: Memory.PendingApply, right: Memory.Function) => false
case (left: Memory.PendingApply, right: Memory.PendingApply) => left.applies == right.applies
case (left: Memory.PendingApply, right: Memory.Range) => false
//Range
case (left: Memory.Range, right: Memory.Remove) => false
case (left: Memory.Range, right: Memory.Put) => false
case (left: Memory.Range, right: Memory.Update) => false
case (left: Memory.Range, right: Memory.Function) => false
case (left: Memory.Range, right: Memory.PendingApply) => false
case (left: Memory.Range, right: Memory.Range) => left.fromValue == right.fromValue && left.rangeValue == right.rangeValue
}
case class Put(key: Slice[Byte],
value: SliceOption[Byte],
deadline: Option[Deadline],
time: Time) extends Memory.Fixed with KeyValue.Put {
override def id: Byte = Put.id
override def isPut: Boolean = true
override def persistentTime: Time = time
override def getPut: KeyValue.Put = this
override def mergedKey = key
override def isRemoveRangeMayBe = false
override def indexEntryDeadline: Option[Deadline] = deadline
def unslice(): Memory.Put =
if (key.isOriginalFullSlice && value.isUnslicedOption && time.time.isOriginalFullSlice)
this
else
Put(
key = key.unslice(),
value = value.unsliceOption(),
deadline = deadline,
time = time.unslice()
)
override def valueLength: Int =
value.mapC(_.size).getOrElse(0)
def hasTimeLeft(): Boolean =
deadline.forall(_.hasTimeLeft())
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean =
deadline.forall(deadline => (deadline - minus).hasTimeLeft())
override def getOrFetchValue: SliceOption[Byte] =
if (value.existsC(_.isEmpty))
Slice.Null
else
value
override def toFromValue(): Value.Put =
Value.Put(value, deadline, time)
override def copyWithDeadlineAndTime(deadline: Option[Deadline],
time: Time): Put =
copy(deadline = deadline, time = time)
override def copyWithTime(time: Time): Put =
copy(time = time)
//to do - make type-safe.
override def toRangeValue(): Value.RangeValue =
throw IO.throwable("Put cannot be converted to RangeValue")
override def toMemory: Memory.Put =
this
}
object Update {
final val id = 2.toByte
}
case class Update(key: Slice[Byte],
value: SliceOption[Byte],
deadline: Option[Deadline],
time: Time) extends KeyValue.Update with Memory.Fixed {
override def id: Byte = Update.id
override def isPut: Boolean = false
override def persistentTime: Time = time
override def isRemoveRangeMayBe = false
override def mergedKey = key
override def indexEntryDeadline: Option[Deadline] = deadline
def unslice(): Memory.Update =
if (key.isOriginalFullSlice && value.isUnslicedOption && time.time.isOriginalFullSlice)
this
else
Update(
key = key.unslice(),
value = value.unsliceOption(),
deadline = deadline,
time = time.unslice()
)
def hasTimeLeft(): Boolean =
deadline.forall(_.hasTimeLeft())
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean =
deadline.forall(deadline => (deadline - minus).hasTimeLeft())
override def getOrFetchValue: SliceOption[Byte] =
if (value.existsC(_.isEmpty))
Slice.Null
else
value
override def toFromValue(): Value.Update =
Value.Update(value, deadline, time)
override def copyWithDeadlineAndTime(deadline: Option[Deadline],
time: Time): Update =
copy(deadline = deadline, time = time)
override def copyWithTime(time: Time): Update =
copy(time = time)
override def copyWithDeadline(deadline: Option[Deadline]): Update =
copy(deadline = deadline)
override def toPut(): Memory.Put =
Memory.Put(
key = key,
value = value,
deadline = deadline,
time = time
)
override def toPut(deadline: Option[Deadline]): Memory.Put =
Memory.Put(
key = key,
value = value,
deadline = deadline,
time = time
)
override def toRangeValue(): Value.Update =
toFromValue()
override def toMemory: Memory.Update =
this
}
object Function {
final val id = 3.toByte
}
case class Function(key: Slice[Byte],
function: Slice[Byte],
time: Time) extends KeyValue.Function with Memory.Fixed {
override def id: Byte = Function.id
override def isPut: Boolean = false
override def persistentTime: Time = time
override def mergedKey = key
override def isRemoveRangeMayBe = false
override def indexEntryDeadline: Option[Deadline] = None
override def value: SliceOption[Byte] = function
override def deadline: Option[Deadline] = None
def unslice(): Memory.Function =
if (key.isOriginalFullSlice && function.isOriginalFullSlice && time.time.isOriginalFullSlice)
this
else
Function(
key = key.unslice(),
function = function.unslice(),
time = time.unslice()
)
override def getOrFetchFunction: Slice[Byte] =
function
override def toFromValue(): Value.Function =
Value.Function(function, time)
override def copyWithTime(time: Time): Function =
copy(time = time)
override def toRangeValue(): Value.Function =
toFromValue()
override def toMemory: Memory.Function =
this
}
object PendingApply {
final val id = 4.toByte
}
case class PendingApply(key: Slice[Byte],
applies: Slice[Value.Apply]) extends KeyValue.PendingApply with Memory.Fixed {
override def id: Byte = PendingApply.id
override def persistentTime: Time = time
override def isPut: Boolean = false
override def mergedKey = key
override def isRemoveRangeMayBe = false
override lazy val value: SliceOption[Byte] = ValueSerializer.writeBytes(applies)
def unslice(): Memory.PendingApply =
if (key.isOriginalFullSlice && applies.forall(_.isUnsliced))
this
else
PendingApply(
key = key.unslice(),
applies = applies.map(_.unslice)
)
override val deadline =
None
override def indexEntryDeadline: Option[Deadline] = deadline
def time = Time.fromApplies(applies)
override def getOrFetchApplies: Slice[Value.Apply] =
applies
override def toFromValue(): Value.PendingApply =
Value.PendingApply(applies)
override def toRangeValue(): Value.PendingApply =
toFromValue()
override def toMemory: Memory.PendingApply =
this
}
object Remove {
final val id = 5.toByte
}
case class Remove(key: Slice[Byte],
deadline: Option[Deadline],
time: Time) extends Memory.Fixed with KeyValue.Remove {
override def id: Byte = Remove.id
override def isPut: Boolean = false
override def persistentTime: Time = time
override def mergedKey = key
override def isRemoveRangeMayBe = false
override def indexEntryDeadline: Option[Deadline] = deadline
override def value: SliceOption[Byte] = Slice.Null
override def unslice(): Memory.Remove =
if (key.isOriginalFullSlice && time.time.isOriginalFullSlice)
this
else
Remove(
key = key.unslice(),
deadline = deadline,
time = time.unslice()
)
def hasTimeLeft(): Boolean =
deadline.exists(_.hasTimeLeft())
def hasTimeLeftAtLeast(atLeast: FiniteDuration): Boolean =
deadline.exists(deadline => (deadline - atLeast).hasTimeLeft())
def toRemoveValue(): Value.Remove =
Value.Remove(deadline, time)
override def copyWithTime(time: Time): Remove =
copy(time = time)
override def toFromValue(): Value.Remove =
toRemoveValue()
override def toRangeValue(): Value.Remove =
toFromValue()
override def toMemory: Memory.Remove =
this
}
object Range {
final val id = 6.toByte
def apply(fromKey: Slice[Byte],
toKey: Slice[Byte],
fromValue: Value.FromValue,
rangeValue: Value.RangeValue): Range =
new Range(
fromKey = fromKey,
toKey = toKey,
fromValue = fromValue,
rangeValue = rangeValue
)
}
case class Range(fromKey: Slice[Byte],
toKey: Slice[Byte],
fromValue: Value.FromValueOption,
rangeValue: Value.RangeValue) extends Memory with KeyValue.Range {
override def persistentTime: Time = Time.empty
override def isRemoveRangeMayBe: Boolean = rangeValue.hasRemoveMayBe
override def id: Byte = Range.id
override def isPut: Boolean = false
def isRange: Boolean = true
override def unslice(): Memory.Range =
if (fromKey.isOriginalFullSlice && toKey.isOriginalFullSlice && fromValue.forallS(_.isUnsliced) && rangeValue.isUnsliced)
this
else
Range(
fromKey = fromKey.unslice(),
toKey = toKey.unslice(),
fromValue = fromValue.flatMapS(_.unslice),
rangeValue = rangeValue.unslice
)
override lazy val mergedKey: Slice[Byte] = Bytes.compressJoin(fromKey, toKey)
override lazy val value: SliceOption[Byte] = {
val bytesRequired = OptionRangeValueSerializer.bytesRequired(fromValue, rangeValue)
val bytes = if (bytesRequired == 0) Slice.Null else Slice.of[Byte](bytesRequired)
bytes.foreachC(OptionRangeValueSerializer.write(fromValue, rangeValue, _))
bytes
}
override def deadline: Option[Deadline] = None
override def key: Slice[Byte] = fromKey
override def indexEntryDeadline: Option[Deadline] = None
override def fetchFromValueUnsafe: Value.FromValueOption =
fromValue
override def fetchRangeValueUnsafe: Value.RangeValue =
rangeValue
override def fetchFromAndRangeValueUnsafe: (Value.FromValueOption, Value.RangeValue) =
(fromValue, rangeValue)
override def toMemory: Memory.Range =
this
}
}
private[core] sealed trait PersistentOption extends SomeOrNone[PersistentOption, Persistent] with KeyValueOption {
override def noneS: PersistentOption = Persistent.Null
def asPartial: Persistent.PartialOption =
if (this.isSomeS)
getS
else
Persistent.Partial.Null
}
private[core] sealed trait Persistent extends KeyValue.CacheAble with Persistent.Partial with PersistentOption {
def indexOffset: Int
def nextIndexOffset: Int
def nextKeySize: Int
def sortedIndexAccessPosition: Int
/**
* If key-value is read from copied HashIndex then keyValue.nextKeySize can be 0 (unknown) so always
* use nextIndexOffset to determine is there are more key-values.
*/
def hasMore: Boolean =
nextIndexOffset > -1
override def isPartial: Boolean =
false
def valueLength: Int
def valueOffset: Int
def toMemory(): Memory
def isValueCached: Boolean
def toMemoryOption(): MemoryOption =
toMemory()
override def isNoneS: Boolean =
false
override def getS: Persistent =
this
override def getUnsafe: Persistent =
getS
/**
* This function is NOT thread-safe and is mutable. It should always be invoke at the time of creation
* and before inserting into the Segment's cache.
*/
def unsliceKeys: Unit
}
private[core] object Persistent {
final case object Null extends PersistentOption with KeyValue.Null {
override val isNoneS: Boolean = true
override def getS: Persistent = throw new Exception("get on Persistent key-value that is none")
override def getUnsafe: KeyValue = getS
}
/**
* [[Partial]] key-values types are used in persistent databases
* for quick search where the entire key-value parse is skipped
* and only key is read for processing.
*/
private[core] sealed trait PartialOption extends SomeOrNoneCovariant[PartialOption, Partial] {
override def noneC: PartialOption = Partial.Null
def toPersistentOptional: PersistentOption =
if (isNoneC)
Persistent.Null
else
getC.toPersistent
}
sealed trait Partial extends PartialOption {
/**
* Flags used to indicated if this key-value was a successful binary search match.
*
* A custom typed object or [[Option]] or [[Either]] type can be used instead of using this flag but Binary search
* is expensive. If [[Option]] was used then search on a million key-values can create almost 20 million (18,609,105)
* of [[Option]] instances in-memory which is expensive. So this mutable primitive boolean flags are used instead
* to be memory efficient.
*/
var isBinarySearchMatched: Boolean = false
var isBinarySearchBehind: Boolean = false
var isBinarySearchAhead: Boolean = false
def key: Slice[Byte]
def indexOffset: Int
def toPersistent: Persistent
def isPartial: Boolean = true
def get: Partial = this
//NOTE: the input key should be full Key and NOT comparable key.
def matchMutateForBinarySearch(key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]): Persistent.Partial
//NOTE: the input key should be full Key and NOT comparable key.
def matchForHashIndex(key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]): Boolean
}
object Partial {
final case object Null extends PartialOption {
override val isNoneC: Boolean = true
override def getC: Partial = throw new Exception("Partial is of type Null")
}
trait Fixed extends Persistent.Partial {
override def getC: Partial = this
override def isNoneC: Boolean = false
def matchMutateForBinarySearch(key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]): Persistent.Partial = {
KeyMatcher.Get.matchMutateForBinarySearch(
key = key,
partialKeyValue = this
)
this
}
def matchForHashIndex(key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]): Boolean =
KeyMatcher.Get.matchForHashIndex(
key = key,
partialKeyValue = this
)
}
trait Range extends Persistent.Partial {
override def getC: Partial = this
override def isNoneC: Boolean = false
def fromKey: Slice[Byte]
def toKey: Slice[Byte]
def matchMutateForBinarySearch(key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]): Persistent.Partial = {
KeyMatcher.Get.matchMutateForBinarySearch(
key = key,
range = this
)
this
}
def matchForHashIndex(key: Slice[Byte])(implicit keyOrder: KeyOrder[Slice[Byte]]): Boolean =
KeyMatcher.Get.matchForHashIndex(
key = key,
range = this
)
}
}
sealed trait Fixed extends Persistent with KeyValue.Fixed with Partial.Fixed
sealed trait Reader[T <: Persistent] {
def apply(key: Slice[Byte],
deadline: Option[Deadline],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): T
}
object Remove extends Reader[Persistent.Remove] {
def apply(key: Slice[Byte],
deadline: Option[Deadline],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.Remove =
Persistent.Remove(
_key = key,
indexOffset = indexOffset,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
deadline = deadline,
sortedIndexAccessPosition = sortedIndexAccessPosition,
_time = time
)
}
case class Remove(private var _key: Slice[Byte],
deadline: Option[Deadline],
private var _time: Time,
indexOffset: Int,
nextIndexOffset: Int,
nextKeySize: Int,
sortedIndexAccessPosition: Int) extends Persistent.Fixed with KeyValue.Remove {
override val valueLength: Int = 0
override val isValueCached: Boolean = true
override val valueOffset: Int = -1
def key = _key
def time = _time
override def indexEntryDeadline: Option[Deadline] = deadline
override def unsliceKeys(): Unit = {
_key = _key.unslice()
_time = _time.unslice()
}
def hasTimeLeft(): Boolean =
deadline.exists(_.hasTimeLeft())
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean =
deadline.exists(deadline => (deadline - minus).hasTimeLeft())
override def toMemory(): Memory.Remove =
Memory.Remove(
key = key,
deadline = deadline,
time = time
)
override def copyWithTime(time: Time): Remove =
copy(_time = time)
override def toFromValue(): Value.Remove =
toRemoveValue()
override def toRangeValue(): Value.Remove =
toFromValue()
override def toRemoveValue(): Value.Remove =
Value.Remove(deadline, time)
def toPersistent: Persistent.Remove =
this
}
object Put extends Reader[Persistent.Put] {
def apply(key: Slice[Byte],
deadline: Option[Deadline],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.Put =
new Put(
_key = key,
deadline = deadline,
valueCache =
Cache.noIO[ValuesBlock.Offset, SliceOption[Byte]](synchronised = true, stored = true, initial = None) {
(offset, _) =>
if (offset.size == 0)
Slice.Null
else if (valuesReaderOrNull == null)
throw IO.throwable("ValuesBlock is undefined.")
else
UnblockedReader.moveTo(offset, valuesReaderOrNull)
.copy()
.readFullBlockOrNone()
.unsliceOption()
},
_time = time,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
}
case class Put(private var _key: Slice[Byte],
deadline: Option[Deadline],
private val valueCache: CacheNoIO[ValuesBlock.Offset, SliceOption[Byte]],
private var _time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int) extends Persistent.Fixed with KeyValue.Put {
override def unsliceKeys: Unit = {
_key = _key.unslice()
_time = _time.unslice()
}
override def key: Slice[Byte] =
_key
override def time: Time =
_time
override def getPut: KeyValue.Put =
this
override def indexEntryDeadline: Option[Deadline] = deadline
def hasTimeLeft(): Boolean =
deadline.forall(_.hasTimeLeft())
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean =
deadline.forall(deadline => (deadline - minus).hasTimeLeft())
override def getOrFetchValue: SliceOption[Byte] =
valueCache.value(ValuesBlock.Offset(valueOffset, valueLength))
override def isValueCached: Boolean =
valueCache.isCached
override def toFromValue(): Value.Put =
Value.Put(
value = getOrFetchValue,
deadline = deadline,
time = time
)
override def toRangeValue(): Value.RangeValue =
throw IO.throwable("Put cannot be converted to RangeValue")
override def toMemory(): Memory.Put =
Memory.Put(
key = key,
value = getOrFetchValue,
deadline = deadline,
time = time
)
override def copyWithDeadlineAndTime(deadline: Option[Deadline],
time: Time): Put =
copy(deadline = deadline, _time = time)
override def copyWithTime(time: Time): Put =
copy(_time = time)
def toPersistent: Persistent.Put =
this
}
object Update extends Reader[Persistent.Update] {
def apply(key: Slice[Byte],
deadline: Option[Deadline],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.Update =
new Update(
_key = key,
deadline = deadline,
valueCache =
Cache.noIO[ValuesBlock.Offset, SliceOption[Byte]](synchronised = true, stored = true, initial = None) {
(offset, _) =>
if (offset.size == 0)
Slice.Null
else if (valuesReaderOrNull == null)
throw IO.throwable("ValuesBlock is undefined.")
else
UnblockedReader.moveTo(offset, valuesReaderOrNull)
.copy()
.readFullBlockOrNone()
.unsliceOption()
},
_time = time,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
}
case class Update(private var _key: Slice[Byte],
deadline: Option[Deadline],
private val valueCache: CacheNoIO[ValuesBlock.Offset, SliceOption[Byte]],
private var _time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int) extends Persistent.Fixed with KeyValue.Update {
override def unsliceKeys: Unit = {
_key = _key.unslice()
_time = _time.unslice()
}
override def key: Slice[Byte] =
_key
override def time: Time =
_time
override def indexEntryDeadline: Option[Deadline] = deadline
def hasTimeLeft(): Boolean =
deadline.forall(_.hasTimeLeft())
def hasTimeLeftAtLeast(minus: FiniteDuration): Boolean =
deadline.forall(deadline => (deadline - minus).hasTimeLeft())
override def isValueCached: Boolean =
valueCache.isCached
def getOrFetchValue: SliceOption[Byte] =
valueCache.value(ValuesBlock.Offset(valueOffset, valueLength))
override def toFromValue(): Value.Update =
Value.Update(
value = getOrFetchValue,
deadline = deadline,
time = time
)
override def toRangeValue(): Value.Update =
toFromValue()
override def toMemory(): Memory.Update =
Memory.Update(
key = key,
value = getOrFetchValue,
deadline = deadline,
time = time
)
override def copyWithDeadlineAndTime(deadline: Option[Deadline],
time: Time): Update =
copy(deadline = deadline, _time = time)
override def copyWithDeadline(deadline: Option[Deadline]): Update =
copy(deadline = deadline)
override def copyWithTime(time: Time): Update =
copy(_time = time)
override def toPut(): Persistent.Put =
Persistent.Put(
_key = key,
deadline = deadline,
valueCache = valueCache,
_time = time,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
override def toPut(deadline: Option[Deadline]): Persistent.Put =
Persistent.Put(
_key = key,
deadline = deadline,
valueCache = valueCache,
_time = time,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
def toPersistent: Persistent.Update =
this
}
object Function extends Reader[Persistent.Function] {
def apply(key: Slice[Byte],
deadline: Option[Deadline],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.Function =
apply(
key = key,
valuesReaderOrNull = valuesReaderOrNull,
time = time,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
def apply(key: Slice[Byte],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.Function =
new Function(
_key = key,
valueCache =
Cache.noIO[ValuesBlock.Offset, Slice[Byte]](synchronised = true, stored = true, initial = None) {
(offset, _) =>
if (valuesReaderOrNull == null)
throw IO.throwable("ValuesBlock is undefined.")
else
UnblockedReader.moveTo(offset, valuesReaderOrNull)
.copy()
.readFullBlock()
.unslice()
},
_time = time,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
}
case class Function(private var _key: Slice[Byte],
private val valueCache: CacheNoIO[ValuesBlock.Offset, Slice[Byte]],
private var _time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int) extends Persistent.Fixed with KeyValue.Function {
override def unsliceKeys: Unit = {
_key = _key.unslice()
_time = _time.unslice()
}
override def key: Slice[Byte] =
_key
override def time: Time =
_time
override def indexEntryDeadline: Option[Deadline] = None
override def isValueCached: Boolean =
valueCache.isCached
def getOrFetchFunction: Slice[Byte] =
valueCache.value(ValuesBlock.Offset(valueOffset, valueLength))
override def toFromValue(): Value.Function =
Value.Function(
function = getOrFetchFunction,
time = time
)
override def toRangeValue(): Value.Function =
toFromValue()
override def toMemory(): Memory.Function =
Memory.Function(
key = key,
function = getOrFetchFunction,
time = time
)
override def copyWithTime(time: Time): Function =
copy(_time = time)
def toPersistent: Persistent.Function =
this
}
object PendingApply extends Reader[Persistent.PendingApply] {
def apply(key: Slice[Byte],
deadline: Option[Deadline],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.PendingApply =
new PendingApply(
_key = key,
_time = time,
deadline = deadline,
valueCache =
Cache.noIO[ValuesBlock.Offset, Slice[Value.Apply]](synchronised = true, stored = true, initial = None) {
(offset, _) =>
if (valuesReaderOrNull == null) {
throw IO.throwable("ValuesBlock is undefined.")
} else {
val bytes =
UnblockedReader.moveTo(offset, valuesReaderOrNull)
.copy()
.readFullBlock()
ValueSerializer
.read[Slice[Value.Apply]](bytes)
.map(_.unslice)
}
},
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
}
case class PendingApply(private var _key: Slice[Byte],
private var _time: Time,
deadline: Option[Deadline],
valueCache: CacheNoIO[ValuesBlock.Offset, Slice[Value.Apply]],
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int) extends Persistent.Fixed with KeyValue.PendingApply {
override def unsliceKeys: Unit = {
_key = _key.unslice()
_time = _time.unslice()
}
override def key: Slice[Byte] =
_key
override def time: Time =
_time
override def indexEntryDeadline: Option[Deadline] = deadline
override def isValueCached: Boolean =
valueCache.isCached
override def getOrFetchApplies: Slice[Value.Apply] =
valueCache.value(ValuesBlock.Offset(valueOffset, valueLength))
override def toFromValue(): Value.PendingApply = {
val applies = valueCache.value(ValuesBlock.Offset(valueOffset, valueLength))
Value.PendingApply(applies)
}
override def toRangeValue(): Value.PendingApply =
toFromValue()
override def toMemory(): Memory.PendingApply =
Memory.PendingApply(
key = key,
applies = getOrFetchApplies
)
def toPersistent: Persistent.PendingApply =
this
}
object Range extends Reader[Persistent.Range] {
def apply(key: Slice[Byte],
deadline: Option[Deadline],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
time: Time,
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.Range =
apply(
key = key,
valuesReaderOrNull = valuesReaderOrNull,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
def apply(key: Slice[Byte],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Range = {
val (fromKey, toKey) = Bytes.decompressJoin(key)
Range.parsedKey(
fromKey = fromKey,
toKey = toKey,
valuesReaderOrNull = valuesReaderOrNull,
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
}
def parsedKey(fromKey: Slice[Byte],
toKey: Slice[Byte],
valuesReaderOrNull: UnblockedReader[ValuesBlock.Offset, ValuesBlock],
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int): Persistent.Range =
Range(
_fromKey = fromKey,
_toKey = toKey,
valueCache =
Cache.noIO[ValuesBlock.Offset, (Value.FromValueOption, Value.RangeValue)](synchronised = true, stored = true, initial = None) {
(offset, _) =>
if (valuesReaderOrNull == null) {
throw IO.throwable("ValuesBlock is undefined.")
} else {
val bytes =
UnblockedReader.moveTo(offset, valuesReaderOrNull)
.copy()
.readFullBlock()
val (from, range) =
RangeValueSerializer.read(bytes)
(from.flatMapS(_.unslice), range.unslice)
}
},
nextIndexOffset = nextIndexOffset,
nextKeySize = nextKeySize,
indexOffset = indexOffset,
valueOffset = valueOffset,
valueLength = valueLength,
sortedIndexAccessPosition = sortedIndexAccessPosition
)
}
case class Range private(private var _fromKey: Slice[Byte],
private var _toKey: Slice[Byte],
valueCache: CacheNoIO[ValuesBlock.Offset, (Value.FromValueOption, Value.RangeValue)],
nextIndexOffset: Int,
nextKeySize: Int,
indexOffset: Int,
valueOffset: Int,
valueLength: Int,
sortedIndexAccessPosition: Int) extends Persistent with KeyValue.Range with Partial.Range {
def fromKey = _fromKey
def toKey = _toKey
override def indexEntryDeadline: Option[Deadline] = None
override def unsliceKeys: Unit = {
this._fromKey = _fromKey.unslice()
this._toKey = _toKey.unslice()
}
override def key: Slice[Byte] =
_fromKey
def fetchRangeValueUnsafe: Value.RangeValue =
fetchFromAndRangeValueUnsafe._2
def fetchFromValueUnsafe: Value.FromValueOption =
fetchFromAndRangeValueUnsafe._1
def fetchFromAndRangeValueUnsafe: (Value.FromValueOption, Value.RangeValue) =
valueCache.value(ValuesBlock.Offset(valueOffset, valueLength))
override def toMemory(): Memory.Range = {
val (fromValue, rangeValue) = fetchFromAndRangeValueUnsafe
Memory.Range(
fromKey = fromKey,
toKey = toKey,
fromValue = fromValue,
rangeValue = rangeValue
)
}
override def isValueCached: Boolean =
valueCache.isCached
def toPersistent: Persistent.Range =
this
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy