packages feed

apecs-roaring-1.0: src/Apecs/Stores/Roaring.hs

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

{-| Keeps an off-heap Roaring 'Index' of a store's members next to the store itself.

Reads go straight to the wrapped store, so @Roaring (Cache n (Map c))@ keeps the cache's lookup speed.
Members come from the index, which lets queries over several indexed stores run as bitmap operations.

Only stores that behave like a plain map can be wrapped ('Cachable'), since the index must see every write and removal.
Put @Roaring@ outside any 'Apecs.Stores.Cache'.
-}
module Apecs.Stores.Roaring
  ( Roaring (..)
  ) where

import Apecs.Core (Elem, ExplDestroy (..), ExplGet (..), ExplInit (..), ExplMembers (..), ExplSet (..))
import Apecs.Stores (Cachable)
import Apecs.Stores.Index (HasIndex (..), Index)
import Apecs.Stores.Index qualified as Index
import Control.Monad.IO.Class (MonadIO (..))

data Roaring s = Roaring {-# UNPACK #-} !Index !s

type instance Elem (Roaring s) = Elem s

instance (MonadIO m, ExplInit m s, Cachable s) => ExplInit m (Roaring s) where
  explInit = Roaring <$> liftIO Index.new <*> explInit

instance (ExplGet m s) => ExplGet m (Roaring s) where
  explGet (Roaring _ s) = explGet s
  {-# INLINE explGet #-}
  explExists (Roaring _ s) = explExists s
  {-# INLINE explExists #-}

instance (MonadIO m, ExplSet m s) => ExplSet m (Roaring s) where
  explSet (Roaring index s) ety c = do
    explSet s ety c
    liftIO $ Index.insert index ety
  {-# INLINE explSet #-}

instance (MonadIO m, ExplDestroy m s) => ExplDestroy m (Roaring s) where
  explDestroy (Roaring index s) ety = do
    explDestroy s ety
    liftIO $ Index.delete index ety
  {-# INLINE explDestroy #-}

instance (MonadIO m) => ExplMembers m (Roaring s) where
  explMembers (Roaring index _) = liftIO $ Index.toVector index
  {-# INLINE explMembers #-}

instance HasIndex (Roaring s) where
  getIndex (Roaring index _) = index