packages feed

zydiskell-0.1.0.0: src/Zydis/Util.hs

-- Util.hs ---

-- Copyright (C) 2020 Nerd Ed

-- Author: Nerd Ed <nerded.nerded@gmail.com>

-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 3
-- of the License, or (at your option) any later version.

-- This program 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 General Public License for more details.

-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.

{-# LANGUAGE DerivingStrategies  #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE PolyKinds           #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}

-- |
-- = This handy module extend Storable typeclasse with default instances for C-like enums/fixed arrays (FFI).
--
-- Using 'StorableExt', we are now able to use deriving via clause on sum types.
--
-- @
-- data X
--   = A
--   | B
--   | C
--   deriving stock Enum
--   deriving Storable via StorableExt X
-- @
--
-- This type will be stored as a word32 (C enum FFI).
--
-- Using the 'StorableFixedArray', we are now able to encode fixed sizes in the type (in conjunction with storable-record "Foreign.Storable.FixedArray").
--
-- @
-- data X = X (StorableFixedArray Word32 10)
-- @
--
-- This type will be stored as 10 contiguous word32 (C fixed array).
--
module Zydis.Util
  ( StorableExt(..)
  , StorableFixedArray(..)
  , Storable
  )
where

import           Data.Foldable
import           Data.Proxy
import           Data.Vector
import           Data.Word
import           Foreign.Ptr
import           Foreign.Storable
import qualified Foreign.Storable.FixedArray   as Fixed
import           GHC.TypeLits


-- | Wrapper to extend storable default instances.
newtype StorableExt a =
  StorableExt
    { unStorableExt :: a
    }
  deriving stock (Show, Eq)

instance forall a. Enum a => Storable (StorableExt a) where
  alignment = const $ alignment @Word32 undefined
  sizeOf = const $ sizeOf @Word32 undefined
  peek = fmap (StorableExt . toEnum . fromIntegral) . peek . castPtr @_ @Word32
  poke ptr v =
    poke (castPtr @_ @Word32 ptr) (fromIntegral $ fromEnum $ unStorableExt v)

-- | Wrapper to extend storable default instances.
newtype StorableFixedArray a b =
  StorableFixedArray
    { unStorableFixedArray :: Vector a
    }
  deriving stock (Show, Eq)

instance forall a b. (Storable a, KnownNat b) => Storable (StorableFixedArray a b) where
  alignment = const $ alignment @a undefined
  sizeOf =
    const $ Fixed.sizeOfArray @a (fromIntegral $ natVal (Proxy @b)) undefined
  peek ptr = StorableFixedArray <$> Fixed.run ptr' loop
   where
    ptr' = castPtr ptr
    loop = replicateM (fromIntegral $ natVal (Proxy @b)) Fixed.peekNext
  poke ptr x = Fixed.run ptr' $ loop $ unStorableFixedArray x
   where
    ptr' = castPtr ptr
    loop = traverse_ Fixed.pokeNext