packages feed

eventlog-live-otelcol-0.6.1.0: src/GHC/Eventlog/Live/Otelcol/Processor/Common/SymbolTable.hs

{- |
Module      : GHC.Eventlog.Live.Otelcol.Processor.Common.SymbolTable
Description : Abstract symbol table datatype.
Stability   : experimental
Portability : portable
-}
module GHC.Eventlog.Live.Otelcol.Processor.Common.SymbolTable (
  SymbolIndex,
  SymbolTable,
  empty,
  elemIndex,
  toList,
  fromList,
  insert,
)
where

import Data.Int (Int32)
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import GHC.Generics (Generic)

type SymbolIndex = Int32

data SymbolTable a
  = SymbolTable
  { nextSymbolIndex :: !SymbolIndex
  {- ^
  The next unused `SymbolIndex`.

  > st.nextSymbolIndex == length st.entriesReversed
  -}
  , entryToSymbolIndex :: !(Map a SymbolIndex)
  {- ^
  A map from entries to their `SymbolIndex`.

  > toList st !! (st.entryToSymbolIndex Map.! a) == a
  -}
  , entriesReversed :: ![a] -- reverse order of insertion into entryToSymbolIndex

  {- ^
  A list of entries in the `SymbolTable` in reverse order.
  -}
  }
  deriving (Show, Ord, Eq, Generic)

empty :: SymbolTable a
empty =
  SymbolTable
    { nextSymbolIndex = 0
    , entryToSymbolIndex = Map.empty
    , entriesReversed = []
    }

elemIndex :: (Ord a) => a -> SymbolTable a -> Maybe SymbolIndex
elemIndex a st = Map.lookup a st.entryToSymbolIndex

toList :: SymbolTable a -> [a]
toList st = reverse st.entriesReversed

fromList :: (Ord a) => [a] -> SymbolTable a
fromList = foldr (\val st -> snd (insert val st)) empty

insert :: (Ord a) => a -> SymbolTable a -> (SymbolIndex, SymbolTable a)
insert a st = (si, st'')
 where
  ((si, isNew, st'), entryToSymbolIndex') = Map.alterF (updateEntry st) a st.entryToSymbolIndex
  entriesReversed' = if isNew then a : st'.entriesReversed else st'.entriesReversed
  st'' = st'{entryToSymbolIndex = entryToSymbolIndex', entriesReversed = entriesReversed'}

updateEntry :: SymbolTable a -> Maybe SymbolIndex -> ((SymbolIndex, Bool, SymbolTable a), Maybe SymbolIndex)
updateEntry st Nothing = let (si, st') = freshSymbolIndex st in ((si, True, st'), Just si)
updateEntry st (Just si) = ((si, False, st), Just si)

freshSymbolIndex :: SymbolTable a -> (SymbolIndex, SymbolTable a)
freshSymbolIndex st = (st.nextSymbolIndex, st{nextSymbolIndex = st.nextSymbolIndex + 1})