packages feed

kb-text-shape-0.1.0.0: src/KB/Text/Shape/FFI/Allocator.hsc

module KB.Text.Shape.FFI.Allocator where

import Control.Exception

import Foreign
import Foreign.C

#include "kb_text_shape.h"

{- $doc
@
  typedef void kbts_allocator_function(void *Data, kbts_allocator_op *Op);
    [Data] the custom data pointer you passed in along with your allocator.
    [Op]   the memory request. It is of this type:

      :kbts_allocator_op
      :allocator_op
      typedef struct kbts_allocator_op
      {
        kbts_allocator_op_kind Kind;

        union
        {
          kbts_allocator_op_allocate Allocate;
          kbts_allocator_op_free Free;
        };
      } kbts_allocator_op;
@

And the possible op kinds are:
  KBTS_ALLOCATOR_OP_KIND_ALLOCATE
  KBTS_ALLOCATOR_OP_KIND_FREE

ALLOCATE expects you to fill in Op->Allocate.Pointer.
  The allocation does not need to be aligned.
FREE expects you to free Op->Free.Pointer.
-}

-- | @void kbts_allocator_function(void *Data, kbts_allocator_op *Op);@
type Allocator = Ptr () -> Ptr Op -> IO ()

foreign import ccall "wrapper"
  mkAllocator :: Allocator -> IO (FunPtr Allocator)

freeAllocator :: FunPtr Allocator -> IO ()
freeAllocator = freeHaskellFunPtr

withAllocator :: Allocator -> (FunPtr Allocator -> IO a) -> IO a
withAllocator fun = bracket (mkAllocator fun) freeAllocator

newtype OpKind = OpKind CInt
  deriving (Eq, Ord, Show)
  deriving newtype (Storable)

pattern OP_KIND_NONE :: OpKind
pattern OP_KIND_NONE = OpKind (#const KBTS_ALLOCATOR_OP_KIND_NONE)

pattern OP_KIND_ALLOCATE :: OpKind
pattern OP_KIND_ALLOCATE = OpKind (#const KBTS_ALLOCATOR_OP_KIND_ALLOCATE)

pattern OP_KIND_FREE :: OpKind
pattern OP_KIND_FREE = OpKind (#const KBTS_ALLOCATOR_OP_KIND_FREE)

data Op = Op
  { kind :: OpKind
  , pointer :: Ptr ()
  , size :: Word32 -- ^ only for 'ALLOCATOR_OP_KIND_ALLOCATE'
  } deriving (Eq, Show)

opUnionBase :: Int
opUnionBase = max (sizeOf (undefined :: OpKind)) (alignment (undefined :: Ptr ()))

instance Storable Op where
  alignment ~_ = #alignment kbts_allocator_op
  sizeOf ~_ = #size kbts_allocator_op

  peek ptr = do
    kind <- (#peek kbts_allocator_op, Kind) ptr
    case kind of
      OP_KIND_ALLOCATE -> do
        pointer <- peekByteOff ptr $ opUnionBase + (#offset kbts_allocator_op_allocate, Pointer)
        size <- peekByteOff ptr $ opUnionBase + (#offset kbts_allocator_op_allocate, Size)
        pure $ Op kind pointer size
      OP_KIND_FREE -> do
        pointer <- peekByteOff ptr $ opUnionBase + (#offset kbts_allocator_op_allocate, Pointer)
        pure $ Op kind pointer 0
      _none_etc -> do
        pure $ Op kind nullPtr 0

  poke ptr Op{..} = do
    (#poke kbts_allocator_op, Kind) ptr kind
    case kind of
      OP_KIND_ALLOCATE -> do
        pokeByteOff ptr (opUnionBase + (#offset kbts_allocator_op_allocate, Pointer)) pointer
        pokeByteOff ptr (opUnionBase + (#offset kbts_allocator_op_allocate, Size)) size
      OP_KIND_FREE ->
        pokeByteOff ptr (opUnionBase + (#offset kbts_allocator_op_allocate, Pointer)) pointer
      _ ->
        pure ()