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

swaydb.core.segment.SegmentSerialiser.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.segment

import java.nio.charset.StandardCharsets
import java.nio.file.Paths
import java.util.concurrent.TimeUnit

import swaydb.core.actor.ByteBufferSweeper.ByteBufferSweeperActor
import swaydb.core.actor.FileSweeper.FileSweeperActor
import swaydb.core.actor.MemorySweeper
import swaydb.core.function.FunctionStore
import swaydb.core.io.file.{BlockCache, Effect, ForceSaveApplier}
import swaydb.core.util.{BlockCacheFileIDGenerator, Bytes, Extension, MinMax}
import swaydb.data.MaxKey
import swaydb.data.config.MMAP
import swaydb.data.order.{KeyOrder, TimeOrder}
import swaydb.data.slice.{ReaderBase, Slice}
import swaydb.data.util.ByteSizeOf
import swaydb.data.util.Options._

import scala.concurrent.duration.Deadline

private[core] sealed trait SegmentSerialiser {

  def write(value: Segment, bytes: Slice[Byte]): Unit

  def read(reader: ReaderBase[Byte],
           mmapSegment: MMAP.Segment,
           checkExists: Boolean)(implicit keyOrder: KeyOrder[Slice[Byte]],
                                 timeOrder: TimeOrder[Slice[Byte]],
                                 functionStore: FunctionStore,
                                 keyValueMemorySweeper: Option[MemorySweeper.KeyValue],
                                 fileSweeper: FileSweeperActor,
                                 bufferCleaner: ByteBufferSweeperActor,
                                 blockCache: Option[BlockCache.State],
                                 forceSaveApplier: ForceSaveApplier,
                                 segmentIO: SegmentIO): Segment

  def bytesRequired(value: Segment): Int

}

private[core] object SegmentSerialiser {

  object FormatA extends SegmentSerialiser {
    val formatId: Byte = 0.toByte

    override def write(segment: Segment, bytes: Slice[Byte]): Unit = {
      val segmentPath = Slice(segment.path.toString.getBytes(StandardCharsets.UTF_8))

      val (maxKeyId, maxKeyBytes) =
        segment.maxKey match {
          case MaxKey.Fixed(maxKey) =>
            (1, maxKey)

          case MaxKey.Range(fromKey, maxToKey) =>
            (2, Bytes.compressJoin(fromKey, maxToKey))
        }

      bytes
        .add(this.formatId)
        .add(segment.formatId)
        .addUnsignedInt(segmentPath.size)
        .addAll(segmentPath)
        .addUnsignedInt(segment.createdInLevel)
        .addUnsignedInt(segment.segmentSize)
        .addUnsignedInt(segment.minKey.size)
        .addAll(segment.minKey)
        .addUnsignedInt(maxKeyId)
        .addUnsignedInt(maxKeyBytes.size)
        .addAll(maxKeyBytes)
        .addUnsignedLong(segment.nearestPutDeadline.valueOrElse(_.time.toNanos, 0L))

      segment.minMaxFunctionId match {
        case Some(minMaxFunctionId) =>
          bytes addUnsignedInt minMaxFunctionId.min.size
          bytes addAll minMaxFunctionId.min
          minMaxFunctionId.max match {
            case Some(max) =>
              bytes addUnsignedInt max.size
              bytes addAll max

            case None =>
              bytes addUnsignedInt 0
          }

        case None =>
          bytes addUnsignedInt 0
      }
    }

    def read(reader: ReaderBase[Byte],
             mmapSegment: MMAP.Segment,
             checkExists: Boolean)(implicit keyOrder: KeyOrder[Slice[Byte]],
                                   timeOrder: TimeOrder[Slice[Byte]],
                                   functionStore: FunctionStore,
                                   keyValueMemorySweeper: Option[MemorySweeper.KeyValue],
                                   fileSweeper: FileSweeperActor,
                                   bufferCleaner: ByteBufferSweeperActor,
                                   blockCache: Option[BlockCache.State],
                                   forceSaveApplier: ForceSaveApplier,
                                   segmentIO: SegmentIO): Segment = {

      val formatId = reader.get() //formatId

      if (formatId != this.formatId)
        throw new Exception(s"Invalid serialised Segment formatId: $formatId")

      val segmentFormatId = reader.get()
      val segmentPathLength = reader.readUnsignedInt()
      val segmentPathBytes = reader.read(segmentPathLength).unslice()
      val segmentPath = Paths.get(new String(segmentPathBytes.toArray[Byte], StandardCharsets.UTF_8))
      val createdInLevel = reader.readUnsignedInt()
      val segmentSize = reader.readUnsignedInt()
      val minKeyLength = reader.readUnsignedInt()
      val minKey = reader.read(minKeyLength).unslice()
      val maxKeyId = reader.readUnsignedInt()
      val maxKeyLength = reader.readUnsignedInt()
      val maxKeyBytes = reader.read(maxKeyLength).unslice()

      val maxKey =
        if (maxKeyId == 1) {
          MaxKey.Fixed(maxKeyBytes)
        } else {
          val (fromKey, toKey) = Bytes.decompressJoin(maxKeyBytes)
          MaxKey.Range(fromKey, toKey)
        }

      val nearestExpiryDeadline = {
        val deadlineNanos = reader.readUnsignedLong()
        if (deadlineNanos == 0)
          None
        else
          Some(Deadline((deadlineNanos, TimeUnit.NANOSECONDS)))
      }

      val minMaxFunctionId = {
        val minIdSize = reader.readUnsignedInt()
        if (minIdSize == 0)
          None
        else {
          val minId = reader.read(minIdSize)
          val maxIdSize = reader.readUnsignedInt()
          val maxId = if (maxIdSize == 0) None else Some(reader.read(maxIdSize))
          Some(MinMax(minId, maxId))
        }
      }

      val fileType = Effect.numberFileId(segmentPath)._2

      if (fileType != Extension.Seg)
        throw new Exception(s"File is not a Segment. Path: $segmentPath")

      Segment(
        path = segmentPath,
        formatId = segmentFormatId,
        createdInLevel = createdInLevel,
        blockCacheFileId = BlockCacheFileIDGenerator.nextID,
        mmap = mmapSegment,
        minKey = minKey,
        maxKey = maxKey,
        segmentSize = segmentSize,
        minMaxFunctionId = minMaxFunctionId,
        nearestExpiryDeadline = nearestExpiryDeadline,
        copiedFrom = None,
        checkExists = checkExists
      )
    }

    override def bytesRequired(segment: Segment): Int = {
      val segmentPath = segment.path.toString.getBytes(StandardCharsets.UTF_8)

      val (maxKeyId, maxKeyBytes) =
        segment.maxKey match {
          case MaxKey.Fixed(maxKey) =>
            (1, maxKey)
          case MaxKey.Range(fromKey, maxToKey) =>
            (2, Bytes.compressJoin(fromKey, maxToKey))
        }

      val minMaxFunctionIdBytesRequires =
        segment.minMaxFunctionId match {
          case Some(minMax) =>
            Bytes.sizeOfUnsignedInt(minMax.min.size) +
              minMax.min.size +
              Bytes.sizeOfUnsignedInt(minMax.max.valueOrElse(_.size, 0)) +
              minMax.max.valueOrElse(_.size, 0)

          case None =>
            1
        }

      ByteSizeOf.byte + //formatId
        ByteSizeOf.byte + //segmentFormatId
        Bytes.sizeOfUnsignedInt(segmentPath.length) +
        segmentPath.length +
        Bytes.sizeOfUnsignedInt(segment.createdInLevel) +
        Bytes.sizeOfUnsignedInt(segment.segmentSize) +
        Bytes.sizeOfUnsignedInt(segment.minKey.size) +
        segment.minKey.size +
        Bytes.sizeOfUnsignedInt(maxKeyId) +
        Bytes.sizeOfUnsignedInt(maxKeyBytes.size) +
        maxKeyBytes.size +
        Bytes.sizeOfUnsignedLong(segment.nearestPutDeadline.valueOrElse(_.time.toNanos, 0L)) +
        minMaxFunctionIdBytesRequires
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy