module GHC.Iface.Ext.Fields
   ( ExtensibleFields (..)
   , FieldName
   , emptyExtensibleFields
   -- * Reading
   , readField
   , readFieldWith
   -- * Writing
   , writeField
   , writeFieldWith
   -- * Deletion
   , deleteField
   )
where

import GHC.Prelude
import GHC.Utils.Binary

import Control.Monad
import Data.Map         ( Map )
import qualified Data.Map as Map
import Control.DeepSeq

type FieldName = String

newtype ExtensibleFields = ExtensibleFields { ExtensibleFields -> Map FieldName BinData
getExtensibleFields :: (Map FieldName BinData) }

instance Binary ExtensibleFields where
  put_ :: WriteBinHandle -> ExtensibleFields -> IO ()
put_ WriteBinHandle
bh (ExtensibleFields Map FieldName BinData
fs) = do
    WriteBinHandle -> Int -> IO ()
forall a. Binary a => WriteBinHandle -> a -> IO ()
put_ WriteBinHandle
bh (Map FieldName BinData -> Int
forall k a. Map k a -> Int
Map.size Map FieldName BinData
fs :: Int)

    -- Put the names of each field, and reserve a space
    -- for a payload pointer after each name:
    [(Bin (RelBinPtr Any), BinData)]
header_entries <- [(FieldName, BinData)]
-> ((FieldName, BinData) -> IO (Bin (RelBinPtr Any), BinData))
-> IO [(Bin (RelBinPtr Any), BinData)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM (Map FieldName BinData -> [(FieldName, BinData)]
forall k a. Map k a -> [(k, a)]
Map.toList Map FieldName BinData
fs) (((FieldName, BinData) -> IO (Bin (RelBinPtr Any), BinData))
 -> IO [(Bin (RelBinPtr Any), BinData)])
-> ((FieldName, BinData) -> IO (Bin (RelBinPtr Any), BinData))
-> IO [(Bin (RelBinPtr Any), BinData)]
forall a b. (a -> b) -> a -> b
$ \(FieldName
name, BinData
dat) -> do
      WriteBinHandle -> FieldName -> IO ()
forall a. Binary a => WriteBinHandle -> a -> IO ()
put_ WriteBinHandle
bh FieldName
name
      Bin (RelBinPtr Any)
field_p_p <- WriteBinHandle -> IO (Bin (RelBinPtr Any))
forall {k} (a :: k). WriteBinHandle -> IO (Bin a)
tellBinWriter WriteBinHandle
bh
      WriteBinHandle -> Bin (RelBinPtr Any) -> IO ()
forall a. Binary a => WriteBinHandle -> a -> IO ()
put_ WriteBinHandle
bh Bin (RelBinPtr Any)
field_p_p
      (Bin (RelBinPtr Any), BinData) -> IO (Bin (RelBinPtr Any), BinData)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bin (RelBinPtr Any)
field_p_p, BinData
dat)

    -- Now put the payloads and use the reserved space
    -- to point to the start of each payload:
    [(Bin (RelBinPtr Any), BinData)]
-> ((Bin (RelBinPtr Any), BinData) -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [(Bin (RelBinPtr Any), BinData)]
header_entries (((Bin (RelBinPtr Any), BinData) -> IO ()) -> IO ())
-> ((Bin (RelBinPtr Any), BinData) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(Bin (RelBinPtr Any)
field_p_p, BinData
dat) -> do
      Bin Any
field_p <- WriteBinHandle -> IO (Bin Any)
forall {k} (a :: k). WriteBinHandle -> IO (Bin a)
tellBinWriter WriteBinHandle
bh
      WriteBinHandle -> Bin (RelBinPtr Any) -> Bin Any -> IO ()
forall {k} (a :: k).
WriteBinHandle -> Bin (RelBinPtr a) -> Bin a -> IO ()
putAtRel WriteBinHandle
bh Bin (RelBinPtr Any)
field_p_p Bin Any
field_p
      WriteBinHandle -> Bin Any -> IO ()
forall {k} (a :: k). WriteBinHandle -> Bin a -> IO ()
seekBinWriter WriteBinHandle
bh Bin Any
field_p
      WriteBinHandle -> BinData -> IO ()
forall a. Binary a => WriteBinHandle -> a -> IO ()
put_ WriteBinHandle
bh BinData
dat

  get :: ReadBinHandle -> IO ExtensibleFields
get ReadBinHandle
bh = do
    Int
n <- ReadBinHandle -> IO Int
forall a. Binary a => ReadBinHandle -> IO a
get ReadBinHandle
bh :: IO Int

    -- Get the names and field pointers:
    [(FieldName, RelBin Any)]
header_entries <- Int -> IO (FieldName, RelBin Any) -> IO [(FieldName, RelBin Any)]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
n (IO (FieldName, RelBin Any) -> IO [(FieldName, RelBin Any)])
-> IO (FieldName, RelBin Any) -> IO [(FieldName, RelBin Any)]
forall a b. (a -> b) -> a -> b
$
      (,) (FieldName -> RelBin Any -> (FieldName, RelBin Any))
-> IO FieldName -> IO (RelBin Any -> (FieldName, RelBin Any))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadBinHandle -> IO FieldName
forall a. Binary a => ReadBinHandle -> IO a
get ReadBinHandle
bh IO (RelBin Any -> (FieldName, RelBin Any))
-> IO (RelBin Any) -> IO (FieldName, RelBin Any)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ReadBinHandle -> IO (RelBin Any)
forall {k} (a :: k). ReadBinHandle -> IO (RelBin a)
getRelBin ReadBinHandle
bh

    -- Seek to and get each field's payload:
    [(FieldName, BinData)]
fields <- [(FieldName, RelBin Any)]
-> ((FieldName, RelBin Any) -> IO (FieldName, BinData))
-> IO [(FieldName, BinData)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [(FieldName, RelBin Any)]
header_entries (((FieldName, RelBin Any) -> IO (FieldName, BinData))
 -> IO [(FieldName, BinData)])
-> ((FieldName, RelBin Any) -> IO (FieldName, BinData))
-> IO [(FieldName, BinData)]
forall a b. (a -> b) -> a -> b
$ \(FieldName
name, RelBin Any
field_p) -> do
      ReadBinHandle -> RelBin Any -> IO ()
forall {k} (a :: k). ReadBinHandle -> RelBin a -> IO ()
seekBinReaderRel ReadBinHandle
bh RelBin Any
field_p
      BinData
dat <- ReadBinHandle -> IO BinData
forall a. Binary a => ReadBinHandle -> IO a
get ReadBinHandle
bh
      (FieldName, BinData) -> IO (FieldName, BinData)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (FieldName
name, BinData
dat)

    ExtensibleFields -> IO ExtensibleFields
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ExtensibleFields -> IO ExtensibleFields)
-> ([(FieldName, BinData)] -> ExtensibleFields)
-> [(FieldName, BinData)]
-> IO ExtensibleFields
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map FieldName BinData -> ExtensibleFields
ExtensibleFields (Map FieldName BinData -> ExtensibleFields)
-> ([(FieldName, BinData)] -> Map FieldName BinData)
-> [(FieldName, BinData)]
-> ExtensibleFields
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(FieldName, BinData)] -> Map FieldName BinData
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(FieldName, BinData)] -> IO ExtensibleFields)
-> [(FieldName, BinData)] -> IO ExtensibleFields
forall a b. (a -> b) -> a -> b
$ [(FieldName, BinData)]
fields

instance NFData ExtensibleFields where
  rnf :: ExtensibleFields -> ()
rnf (ExtensibleFields Map FieldName BinData
fs) = Map FieldName BinData -> ()
forall a. NFData a => a -> ()
rnf Map FieldName BinData
fs

emptyExtensibleFields :: ExtensibleFields
emptyExtensibleFields :: ExtensibleFields
emptyExtensibleFields = Map FieldName BinData -> ExtensibleFields
ExtensibleFields Map FieldName BinData
forall k a. Map k a
Map.empty

--------------------------------------------------------------------------------
-- | Reading

readField :: Binary a => FieldName -> ExtensibleFields -> IO (Maybe a)
readField :: forall a. Binary a => FieldName -> ExtensibleFields -> IO (Maybe a)
readField FieldName
name = FieldName
-> (ReadBinHandle -> IO a) -> ExtensibleFields -> IO (Maybe a)
forall a.
FieldName
-> (ReadBinHandle -> IO a) -> ExtensibleFields -> IO (Maybe a)
readFieldWith FieldName
name ReadBinHandle -> IO a
forall a. Binary a => ReadBinHandle -> IO a
get

readFieldWith :: FieldName -> (ReadBinHandle -> IO a) -> ExtensibleFields -> IO (Maybe a)
readFieldWith :: forall a.
FieldName
-> (ReadBinHandle -> IO a) -> ExtensibleFields -> IO (Maybe a)
readFieldWith FieldName
name ReadBinHandle -> IO a
read ExtensibleFields
fields = Maybe (IO a) -> IO (Maybe a)
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
forall (m :: * -> *) a. Monad m => Maybe (m a) -> m (Maybe a)
sequence (Maybe (IO a) -> IO (Maybe a)) -> Maybe (IO a) -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ ((ReadBinHandle -> IO a
read =<<) (IO ReadBinHandle -> IO a)
-> (BinData -> IO ReadBinHandle) -> BinData -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BinData -> IO ReadBinHandle
dataHandle) (BinData -> IO a) -> Maybe BinData -> Maybe (IO a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
  FieldName -> Map FieldName BinData -> Maybe BinData
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup FieldName
name (ExtensibleFields -> Map FieldName BinData
getExtensibleFields ExtensibleFields
fields)

--------------------------------------------------------------------------------
-- | Writing

writeField :: Binary a => FieldName -> a -> ExtensibleFields -> IO ExtensibleFields
writeField :: forall a.
Binary a =>
FieldName -> a -> ExtensibleFields -> IO ExtensibleFields
writeField FieldName
name a
x = FieldName
-> (WriteBinHandle -> IO ())
-> ExtensibleFields
-> IO ExtensibleFields
writeFieldWith FieldName
name (WriteBinHandle -> a -> IO ()
forall a. Binary a => WriteBinHandle -> a -> IO ()
`put_` a
x)

writeFieldWith :: FieldName -> (WriteBinHandle -> IO ()) -> ExtensibleFields -> IO ExtensibleFields
writeFieldWith :: FieldName
-> (WriteBinHandle -> IO ())
-> ExtensibleFields
-> IO ExtensibleFields
writeFieldWith FieldName
name WriteBinHandle -> IO ()
write ExtensibleFields
fields = do
  WriteBinHandle
bh <- Int -> IO WriteBinHandle
openBinMem (Int
1024 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1024)
  WriteBinHandle -> IO ()
write WriteBinHandle
bh
  --
  BinData
bd <- WriteBinHandle -> IO BinData
handleData WriteBinHandle
bh
  ExtensibleFields -> IO ExtensibleFields
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ExtensibleFields -> IO ExtensibleFields)
-> ExtensibleFields -> IO ExtensibleFields
forall a b. (a -> b) -> a -> b
$ Map FieldName BinData -> ExtensibleFields
ExtensibleFields (FieldName
-> BinData -> Map FieldName BinData -> Map FieldName BinData
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert FieldName
name BinData
bd (Map FieldName BinData -> Map FieldName BinData)
-> Map FieldName BinData -> Map FieldName BinData
forall a b. (a -> b) -> a -> b
$ ExtensibleFields -> Map FieldName BinData
getExtensibleFields ExtensibleFields
fields)

deleteField :: FieldName -> ExtensibleFields -> ExtensibleFields
deleteField :: FieldName -> ExtensibleFields -> ExtensibleFields
deleteField FieldName
name (ExtensibleFields Map FieldName BinData
fs) = Map FieldName BinData -> ExtensibleFields
ExtensibleFields (Map FieldName BinData -> ExtensibleFields)
-> Map FieldName BinData -> ExtensibleFields
forall a b. (a -> b) -> a -> b
$ FieldName -> Map FieldName BinData -> Map FieldName BinData
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete FieldName
name Map FieldName BinData
fs