{- |
Module          : Foreign.C.Structs.Utils
Description     : Create C structs from Haskell
Copyright       : (c) Simon Plakolb, 2020
License         : MIT
Maintainer      : s.plakolb@gmail.com
Stability       : beta

This module defined some utility functions for Storable instance declarations.
-}
module Foreign.C.Structs.Utils (
      next, fmax, sizeof
) where

import Foreign.Storable (Storable, peek, sizeOf, alignment)
import Foreign.Marshal (alloca)
import Foreign.Ptr (Ptr, plusPtr, alignPtr)

-- | Due to alignment constraints the size of C structs is dependent on the order of fields and their respectible sizes. The function 'sizeof' can calculate the resulting size given a list of all 'alignments' and 'sizes'.
sizeof :: [Int] -> [Int] -> Int
sizeof :: [Int] -> [Int] -> Int
sizeof as :: [Int]
as@(Int
_:[Int]
alignments) (Int
s:[Int]
sizes) = Int -> [Int] -> [Int] -> Int
sizeof' Int
s [Int]
alignments [Int]
sizes
    where
          sizeof' :: Int -> [Int] -> [Int] -> Int
sizeof' Int
s [] [] = Int
s Int -> Int -> Int
forall t. Integral t => t -> t -> t
`pad` [Int] -> Int
forall a. Integral a => [a] -> a
fmax [Int]
as
          sizeof' Int
x (Int
a:[Int]
as) (Int
s:[Int]
ss) = let
                               s' :: Int
s' = Int
xInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
s
                               in Int -> [Int] -> [Int] -> Int
sizeof' (Int
s' Int -> Int -> Int
forall t. Integral t => t -> t -> t
`pad` Int
a) [Int]
as [Int]
ss

          pad :: t -> t -> t
pad t
x t
a
              | t
x t -> t -> t
forall t. Integral t => t -> t -> t
`mod` t
a t -> t -> Bool
forall a. Eq a => a -> a -> Bool
== t
0 = t
x
              | Bool
otherwise      = t -> t -> t
pad (t
xt -> t -> t
forall a. Num a => a -> a -> a
+t
1) t
a

-- | Jumps to the next pointer location in the struct.
next :: (Storable a, Storable b, Storable c) => Ptr a -> b -> IO (Ptr c)
next :: Ptr a -> b -> IO (Ptr c)
next Ptr a
ptr b
x = (Ptr c -> IO (Ptr c)) -> IO (Ptr c)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr c -> IO (Ptr c)) -> IO (Ptr c))
-> (Ptr c -> IO (Ptr c)) -> IO (Ptr c)
forall a b. (a -> b) -> a -> b
$ Ptr a -> b -> Ptr c -> IO (Ptr c)
forall a b c.
(Storable a, Storable b, Storable c) =>
Ptr a -> b -> Ptr c -> IO (Ptr c)
next' Ptr a
ptr b
x
    where
          next' :: (Storable a, Storable b, Storable c) => Ptr a -> b -> Ptr c -> IO (Ptr c)
          next' :: Ptr a -> b -> Ptr c -> IO (Ptr c)
next' Ptr a
ptr b
x Ptr c
ptr_x = do
                let ptr_y :: Ptr b
ptr_y = Ptr a -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
plusPtr Ptr a
ptr (Int -> Ptr b) -> Int -> Ptr b
forall a b. (a -> b) -> a -> b
$ b -> Int
forall a. Storable a => a -> Int
sizeOf b
x
                c
y <- Ptr c -> IO c
forall a. Storable a => Ptr a -> IO a
peek Ptr c
ptr_x
                Ptr c -> IO (Ptr c)
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr c -> IO (Ptr c)) -> Ptr c -> IO (Ptr c)
forall a b. (a -> b) -> a -> b
$ Ptr c -> Int -> Ptr c
forall a. Ptr a -> Int -> Ptr a
alignPtr Ptr c
forall b. Ptr b
ptr_y (Int -> Ptr c) -> Int -> Ptr c
forall a b. (a -> b) -> a -> b
$ c -> Int
forall a. Storable a => a -> Int
alignment c
y

-- | Alias for @foldr max 0@.
fmax :: Integral a => [a] -> a
fmax :: [a] -> a
fmax = (a -> a -> a) -> a -> [a] -> a
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr a -> a -> a
forall a. Ord a => a -> a -> a
max a
0