haskus-binary-1.5: src/lib/Haskus/Memory/Layout.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE NoStarIsType #-}
-- | Memory layout
--
-- Describe a memory region
module Haskus.Memory.Layout
( LPath (..)
, PathElem (..)
, lPath
, LPathType
, LPathOffset
, LRoot
, (:->)
, (:#>)
-- * Layouts
, CPrimitive (..)
, CArray (..)
, CUArray (..)
, CStruct (..)
, CUnion (..)
)
where
import Haskus.Utils.Types
-- | Path in a layout
data LPath (path :: [PathElem]) = LPath
-- | Layout path element
data PathElem
= LIndex Nat -- ^ Addressing via a numeric index
| LSymbol Symbol -- ^ Addressing via a symbol
-- | Layout path root
type LRoot = LPath '[]
-- | Index in the layout path
--
-- Helper for ``ptr --> lPath @p``
-- until
lPath :: forall e. LPath '[e]
lPath = LPath
-- | Type obtained when following path p
type family LPathType p l :: Type
type instance LPathType (LPath '[]) l = l
-- | Offset obtained when following path p
type family LPathOffset p l :: Nat
type instance LPathOffset (LPath '[]) l = 0
type family (:->) p (s :: Symbol) where
(:->) (LPath xs) s = LPath (Snoc xs ('LSymbol s))
type family (:#>) p (n :: Nat) where
(:#>) (LPath xs) n = LPath (Snoc xs ('LIndex n))
---------------------------
-- Layouts
---------------------------
type family CSizeOf a :: Nat
type family CAlignment a :: Nat
-- | Primitives
--
-- >>> :kind! CSizeOf (CPrimitive 8 1)
-- CSizeOf (CPrimitive 8 1) :: Nat
-- = 8
--
-- >>> :kind! CAlignment (CPrimitive 8 2)
-- CAlignment (CPrimitive 8 2) :: Nat
-- = 2
--
data CPrimitive (size :: Nat) (align :: Nat) = CPrimitive
type instance CSizeOf (CPrimitive size align) = size
type instance CAlignment (CPrimitive size align) = align
-- | Array
--
-- >>> type S = CArray 10 (CPrimitive 8 8)
-- >>> :kind! CSizeOf S
-- CSizeOf S :: Nat
-- = 80
--
-- >>> :kind! CAlignment S
-- CAlignment S :: Nat
-- = 8
data CArray (n :: Nat) (a :: k) = CArray
type instance CSizeOf (CArray n a) = n * (CSizeOf a)
type instance CAlignment (CArray n a) = CAlignment a
-- | Unbounded array
--
-- >>> type S = CUArray (CPrimitive 8 8)
-- >>> :kind! CSizeOf S
-- CSizeOf S :: Nat
-- = (TypeError ...)
--
-- >>> :kind! CAlignment S
-- CAlignment S :: Nat
-- = 8
data CUArray (a :: k) = CUArray
type instance CSizeOf (CUArray a) = TypeError ('Text "Cannot apply SizeOf to an unbounded array")
type instance CAlignment (CUArray a) = CAlignment a
-- | Struct
--
-- >>> type S = CStruct ['Field "i8" (CPrimitive 1 1), 'Field "i32" (CPrimitive 4 4)]
-- >>> :kind! CSizeOf S
-- CSizeOf S :: Nat
-- = 8
--
-- >>> :kind! CAlignment S
-- CAlignment S :: Nat
-- = 4
data CStruct (fs :: [Field]) = CStruct
type instance CSizeOf (CStruct fs) = CStructSize fs (CMaxAlignment fs 1) 0
type instance CAlignment (CStruct fs) = CMaxAlignment fs 1
type family CStructSize (xs :: [Field]) al sz where
CStructSize '[] al sz =
sz + PaddingEx (sz `Mod` al) al
CStructSize ('Field s t : fs) al sz = CStructSize fs al
(sz + CSizeOf t + PaddingEx (sz `Mod` CAlignment t) (CAlignment t))
-- | Union
--
-- >>> type S = CUnion ['Field "i8" (CPrimitive 1 1), 'Field "i32" (CPrimitive 4 4)]
-- >>> :kind! CSizeOf S
-- CSizeOf S :: Nat
-- = 4
--
-- >>> :kind! CAlignment S
-- CAlignment S :: Nat
-- = 4
data CUnion (fs :: [Field]) = CUnion
type instance CSizeOf (CUnion fs) = CUnionSize fs (CMaxAlignment fs 1) 0
type instance CAlignment (CUnion fs) = CMaxAlignment fs 1
type family CUnionSize (xs :: [Field]) al sz where
CUnionSize '[] al sz =
sz + PaddingEx (sz `Mod` al) al
CUnionSize ('Field s t : fs) al sz = CUnionSize fs al (Max (CSizeOf t) sz)
-- | Structure field
data Field = Field Symbol Type
type family PaddingEx (m :: Nat) (a :: Nat) where
PaddingEx 0 a = 0
PaddingEx m a = a - m
type family CMaxAlignment (xs :: [Field]) al where
CMaxAlignment '[] al = al
CMaxAlignment ('Field s t : fs) al =
CMaxAlignment fs (Max al (CAlignment t))