packages feed

clr-host-0.2.0: src/Clr/Host/BStr.hs

{-# LANGUAGE CPP, BangPatterns, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses #-}

module Clr.Host.BStr
  ( BStr(..)
  , allocBStr
  , freeBStr
  , withBStr
  ) where

import Control.Exception (bracket)
import Data.Coerce
import Data.Word
import Foreign.Ptr
import Foreign.Storable

import Clr.Host.Config
import Clr.Host.BStr.Type

#ifdef HAVE_MONO
import Clr.Host.BStr.Mono(mono_ptr_to_bstr_hask, mono_free_bstr)
#endif

#ifdef HAVE_DOTNET
import Clr.Host.BStr.DotNet(sysAllocStringLen, sysFreeString)
#endif

import Clr.Marshal

import Data.Text
import Data.Text.Foreign

allocBStr :: (Integral len) => Ptr Word16 -> len -> IO BStr
allocBStr p l = do
  ClrHostConfig hostType <- getClrHostConfig
  case hostType of
#ifdef HAVE_MONO
    ClrHostMono   -> mono_ptr_to_bstr_hask p (fromIntegral l)
#else
    ClrHostMono   -> error "not built with Mono support enabled"
#endif
#ifdef HAVE_DOTNET
    ClrHostDotNet -> sysAllocStringLen p (fromIntegral l)
#else
    ClrHostDotNet -> error "not built with .Net support enabled"
#endif


freeBStr :: BStr -> IO ()
freeBStr x = do
  ClrHostConfig hostType <- getClrHostConfig
  case hostType of
#ifdef HAVE_MONO
    ClrHostMono   -> mono_free_bstr x
#else
    ClrHostMono   -> error "not built with Mono support enabled"
#endif
#ifdef HAVE_DOTNET
    ClrHostDotNet -> sysFreeString x
#else
    ClrHostDotNet -> error "not built with .Net support enabled"
#endif

withBStr :: (Integral len) => Ptr Word16 -> len -> (BStr-> IO a) -> IO a
withBStr p l = bracket (allocBStr p l) freeBStr

bstrToPtrData :: BStr -> Ptr Word16
bstrToPtrData = coerce

bstrToPtrLen :: BStr -> Ptr Word16
bstrToPtrLen bstr = plusPtr (bstrToPtrData bstr) (-4)

bstrLenBytes :: BStr -> IO Word16
bstrLenBytes = peek . bstrToPtrLen

bstrLenChars :: BStr -> IO Word16
bstrLenChars bstr = do
  let charSize = 2
  lenBytes <- bstrLenBytes bstr
  return $ lenBytes `div` charSize

bstrToText :: BStr -> IO Text
bstrToText bstr = do
  let ptrData = bstrToPtrData bstr
  lenChars   <- bstrLenChars  bstr
  fromPtr ptrData $ fromIntegral lenChars

instance {-# OVERLAPPING #-} Marshal Text BStr where
  marshal x f = do
    bstr <- useAsPtr x (\p-> \l-> allocBStr p l)
    !res <- f bstr
    freeBStr bstr
    return res

instance {-# OVERLAPPING #-} Marshal String BStr where
  marshal x f = marshal (pack x) f

instance {-# OVERLAPPING #-} Marshal BStr Text where
  marshal x f = bstrToText x >>= f

instance {-# OVERLAPPING #-} Marshal BStr String where
  marshal x f = marshal x $ \t-> f (unpack t)

instance {-# OVERLAPPING #-} Unmarshal BStr Text where
  unmarshal x = do
    !t <- bstrToText x
    freeBStr x
    return t

instance {-# OVERLAPPING #-} Unmarshal BStr String where
  unmarshal x = unpack <$> unmarshal x

instance {-# OVERLAPPING #-} Unmarshal Text BStr where
  unmarshal x = useAsPtr x (\p-> \l-> allocBStr p l)

instance {-# OVERLAPPING #-} Unmarshal String BStr where
  unmarshal x = unmarshal $ pack x