{-# LANGUAGE DeriveAnyClass      #-}
{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE NamedFieldPuns      #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving  #-}
{-# LANGUAGE TypeFamilies        #-}

-- | Information about the files stored by the volatile DB
--
-- Intended for qualified import.
module Ouroboros.Consensus.Storage.VolatileDB.Impl.FileInfo (
    -- * opaque
    FileInfo
    -- * Construction
  , addBlock
  , empty
  , fromParsedBlockInfos
    -- * Queries
  , canGC
  , hashes
  , isFull
  , maxSlotNo
  , maxSlotNoInFiles
  ) where

import           Data.Set (Set)
import qualified Data.Set as Set
import           GHC.Generics (Generic)
import           NoThunks.Class (NoThunks)

import           Ouroboros.Network.Block (MaxSlotNo (..))

import           Ouroboros.Consensus.Block
import           Ouroboros.Consensus.Storage.VolatileDB.API (BlockInfo (..))
import           Ouroboros.Consensus.Storage.VolatileDB.Impl.Parser
import           Ouroboros.Consensus.Storage.VolatileDB.Impl.Types

{-------------------------------------------------------------------------------
  Types
-------------------------------------------------------------------------------}

-- | The internal information the VolatileDB keeps for each file.
data FileInfo blk = FileInfo {
      FileInfo blk -> MaxSlotNo
maxSlotNo :: !MaxSlotNo
    , FileInfo blk -> Set (HeaderHash blk)
hashes    :: !(Set (HeaderHash blk))
    }
  deriving ((forall x. FileInfo blk -> Rep (FileInfo blk) x)
-> (forall x. Rep (FileInfo blk) x -> FileInfo blk)
-> Generic (FileInfo blk)
forall x. Rep (FileInfo blk) x -> FileInfo blk
forall x. FileInfo blk -> Rep (FileInfo blk) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall blk x. Rep (FileInfo blk) x -> FileInfo blk
forall blk x. FileInfo blk -> Rep (FileInfo blk) x
$cto :: forall blk x. Rep (FileInfo blk) x -> FileInfo blk
$cfrom :: forall blk x. FileInfo blk -> Rep (FileInfo blk) x
Generic)

deriving instance StandardHash blk => Show (FileInfo blk)
deriving instance StandardHash blk => NoThunks (FileInfo blk)

{-------------------------------------------------------------------------------
  Construction
-------------------------------------------------------------------------------}

empty :: FileInfo blk
empty :: FileInfo blk
empty = FileInfo :: forall blk. MaxSlotNo -> Set (HeaderHash blk) -> FileInfo blk
FileInfo {
      maxSlotNo :: MaxSlotNo
maxSlotNo = MaxSlotNo
NoMaxSlotNo
    , hashes :: Set (HeaderHash blk)
hashes  = Set (HeaderHash blk)
forall a. Set a
Set.empty
    }

-- | Adds a block to a 'FileInfo'.
addBlock :: StandardHash blk => SlotNo -> HeaderHash blk -> FileInfo blk -> FileInfo blk
addBlock :: SlotNo -> HeaderHash blk -> FileInfo blk -> FileInfo blk
addBlock SlotNo
slotNo HeaderHash blk
hash FileInfo { MaxSlotNo
maxSlotNo :: MaxSlotNo
maxSlotNo :: forall blk. FileInfo blk -> MaxSlotNo
maxSlotNo, Set (HeaderHash blk)
hashes :: Set (HeaderHash blk)
hashes :: forall blk. FileInfo blk -> Set (HeaderHash blk)
hashes } =
    FileInfo :: forall blk. MaxSlotNo -> Set (HeaderHash blk) -> FileInfo blk
FileInfo {
        maxSlotNo :: MaxSlotNo
maxSlotNo = MaxSlotNo
maxSlotNo MaxSlotNo -> MaxSlotNo -> MaxSlotNo
forall a. Ord a => a -> a -> a
`max` SlotNo -> MaxSlotNo
MaxSlotNo SlotNo
slotNo
      , hashes :: Set (HeaderHash blk)
hashes    = HeaderHash blk -> Set (HeaderHash blk) -> Set (HeaderHash blk)
forall a. Ord a => a -> Set a -> Set a
Set.insert HeaderHash blk
hash Set (HeaderHash blk)
hashes
      }

-- | Construct a 'FileInfo' from the parser result.
fromParsedBlockInfos ::
     forall blk. StandardHash blk
  => [ParsedBlockInfo blk] -> FileInfo blk
fromParsedBlockInfos :: [ParsedBlockInfo blk] -> FileInfo blk
fromParsedBlockInfos [ParsedBlockInfo blk]
parsedBlockInfos = FileInfo :: forall blk. MaxSlotNo -> Set (HeaderHash blk) -> FileInfo blk
FileInfo {
      maxSlotNo :: MaxSlotNo
maxSlotNo = (ParsedBlockInfo blk -> MaxSlotNo)
-> [ParsedBlockInfo blk] -> MaxSlotNo
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap ParsedBlockInfo blk -> MaxSlotNo
parsedBlockInfoToMaxSlotNo [ParsedBlockInfo blk]
parsedBlockInfos
    , hashes :: Set (HeaderHash blk)
hashes    = [HeaderHash blk] -> Set (HeaderHash blk)
forall a. Ord a => [a] -> Set a
Set.fromList ([HeaderHash blk] -> Set (HeaderHash blk))
-> [HeaderHash blk] -> Set (HeaderHash blk)
forall a b. (a -> b) -> a -> b
$ (ParsedBlockInfo blk -> HeaderHash blk)
-> [ParsedBlockInfo blk] -> [HeaderHash blk]
forall a b. (a -> b) -> [a] -> [b]
map (BlockInfo blk -> HeaderHash blk
forall blk. BlockInfo blk -> HeaderHash blk
biHash (BlockInfo blk -> HeaderHash blk)
-> (ParsedBlockInfo blk -> BlockInfo blk)
-> ParsedBlockInfo blk
-> HeaderHash blk
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParsedBlockInfo blk -> BlockInfo blk
forall blk. ParsedBlockInfo blk -> BlockInfo blk
pbiBlockInfo) [ParsedBlockInfo blk]
parsedBlockInfos
    }
  where
    parsedBlockInfoToMaxSlotNo :: ParsedBlockInfo blk -> MaxSlotNo
    parsedBlockInfoToMaxSlotNo :: ParsedBlockInfo blk -> MaxSlotNo
parsedBlockInfoToMaxSlotNo = SlotNo -> MaxSlotNo
MaxSlotNo (SlotNo -> MaxSlotNo)
-> (ParsedBlockInfo blk -> SlotNo)
-> ParsedBlockInfo blk
-> MaxSlotNo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BlockInfo blk -> SlotNo
forall blk. BlockInfo blk -> SlotNo
biSlotNo (BlockInfo blk -> SlotNo)
-> (ParsedBlockInfo blk -> BlockInfo blk)
-> ParsedBlockInfo blk
-> SlotNo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParsedBlockInfo blk -> BlockInfo blk
forall blk. ParsedBlockInfo blk -> BlockInfo blk
pbiBlockInfo

{-------------------------------------------------------------------------------
  Queries
-------------------------------------------------------------------------------}

-- | Checks if this file can be GCed.
canGC ::
     FileInfo blk
  -> SlotNo -- ^ The slot which we want to GC
  -> Bool
canGC :: FileInfo blk -> SlotNo -> Bool
canGC FileInfo { MaxSlotNo
maxSlotNo :: MaxSlotNo
maxSlotNo :: forall blk. FileInfo blk -> MaxSlotNo
maxSlotNo } SlotNo
slot =
    case MaxSlotNo
maxSlotNo of
      MaxSlotNo
NoMaxSlotNo      -> Bool
True
      MaxSlotNo SlotNo
latest -> SlotNo
latest SlotNo -> SlotNo -> Bool
forall a. Ord a => a -> a -> Bool
< SlotNo
slot

-- | Has this file reached its maximum size?
isFull :: BlocksPerFile -> FileInfo blk -> Bool
isFull :: BlocksPerFile -> FileInfo blk -> Bool
isFull BlocksPerFile
maxBlocksPerFile FileInfo { Set (HeaderHash blk)
hashes :: Set (HeaderHash blk)
hashes :: forall blk. FileInfo blk -> Set (HeaderHash blk)
hashes } =
    Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Set (HeaderHash blk) -> Int
forall a. Set a -> Int
Set.size Set (HeaderHash blk)
hashes) Word32 -> Word32 -> Bool
forall a. Ord a => a -> a -> Bool
>= BlocksPerFile -> Word32
unBlocksPerFile BlocksPerFile
maxBlocksPerFile

maxSlotNoInFiles :: [FileInfo blk] -> MaxSlotNo
maxSlotNoInFiles :: [FileInfo blk] -> MaxSlotNo
maxSlotNoInFiles = (FileInfo blk -> MaxSlotNo) -> [FileInfo blk] -> MaxSlotNo
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap FileInfo blk -> MaxSlotNo
forall blk. FileInfo blk -> MaxSlotNo
maxSlotNo