packages feed

vulkan-utils-0.5.11.0: src/Vulkan/Utils/Misc.hs

module Vulkan.Utils.Misc
  ( -- * Sorting things
    partitionOptReq
  , partitionOptReqIO

    -- * Bit Utils
  , showBits
  , (.&&.)
  ) where

import Control.Monad.IO.Class
import Data.Bits
import Data.Foldable
import Data.List
  ( intercalate
  , partition
  )
import Vulkan.Utils.Internal

{- | From a list of things, take all the required things and as many optional
things as possible.
-}
partitionOptReq
  :: (Eq a)
  => [a]
  -- ^ What do we have available
  -> [a]
  -- ^ Optional desired elements
  -> [a]
  -- ^ Required desired elements
  -> ([a], Either [a] [a])
  {- ^ (Missing optional elements, Either (missing required elements) or (all
  required elements and as many optional elements as possible)
  -}
partitionOptReq available optional required =
  let
    (optHave, optMissing) = partition (`elem` available) optional
    (reqHave, reqMissing) = partition (`elem` available) required
  in
    ( optMissing
    , case reqMissing of
        [] -> Right (reqHave <> optHave)
        xs -> Left xs
    )

{- | Like 'partitionOptReq'.

Will throw an 'IOError in the case of missing things. Details on missing
things will be reported in stderr.

This is useful in dealing with layers and extensions.
-}
partitionOptReqIO
  :: (Show a, Eq a, MonadIO m)
  => String
  -- ^ What are we sorting (Used for a debug message)
  -> [a]
  -- ^ What do we have available
  -> [a]
  -- ^ Optional desired elements
  -> [a]
  -- ^ Required desired elements
  -> m ([a], [a])
  {- ^ All the required elements and as many optional elements as possible,
  as well as the missing optional elements.
  -}
partitionOptReqIO type' available optional required = liftIO $ do
  let (optMissing, exts) = partitionOptReq available optional required
  for_ optMissing $
    \o -> sayErr $ "Missing optional " <> type' <> ": " <> show o
  case exts of
    Left reqMissing -> do
      for_ reqMissing $
        \r -> sayErr $ "Missing required " <> type' <> ": " <> show r
      noSuchThing $ "Don't have all required " <> type' <> "s"
    Right xs -> pure (xs, optMissing)

----------------------------------------------------------------

-- * Bit utils

----------------------------------------------------------------

{- | Show valies as a union of their individual bits

>>> showBits @Int 5
"1 .|. 4"

>>> showBits @Int 0
"zeroBits"

>>> import Vulkan.Core10.Enums.QueueFlagBits
>>> showBits (QUEUE_COMPUTE_BIT .|. QUEUE_GRAPHICS_BIT)
"QUEUE_GRAPHICS_BIT .|. QUEUE_COMPUTE_BIT"
-}
showBits :: forall a. (Show a, FiniteBits a) => a -> String
showBits a =
  if a == zeroBits
    then "zeroBits"
    else intercalate " .|. " $ fmap show (setBits a)

setBits :: (FiniteBits a) => a -> [a]
setBits a =
  [ b
  | -- lol, is this really necessary
  p <- [countTrailingZeros a .. finiteBitSize a - countLeadingZeros a - 1]
  , let b = bit p
  , a .&&. b
  ]

-- | Check if the intersection of bits is non-zero
(.&&.) :: (Bits a) => a -> a -> Bool
x .&&. y = (x .&. y) /= zeroBits