-- | A binary-(the library)-like module which communicates directly through
-- handles rather than using bytestring.

{-# LANGUAGE ScopedTypeVariables #-}
module Data.Binary.Storable where

import Control.Monad
import qualified Data.Vector as V
import Foreign.Storable
import Foreign.Marshal.Alloc
import Foreign.Marshal.Utils
import System.IO

type Put a = Handle -> a -> IO ()
type Get a = Handle -> IO a

class Binary a where
  put :: Put a
  get :: Get a

storablePut :: Storable a => Put a
storablePut h a = with a (\ptr -> hPutBuf h ptr (sizeOf a))

storableGet :: forall a. Storable a => Get a
storableGet h = alloca (\ptr -> hGetBuf h ptr (sizeOf (undefined :: a)) >> peek ptr)

instance Binary Int where
  put = storablePut
  get = storableGet

instance Binary a => Binary [a] where
  put h as = put h (length as) >> forM_ as (put h)
  get h = get h >>= \n -> replicateM n (get h)

instance Binary a => Binary (V.Vector a) where
  put h = put h . V.toList
  get h = V.fromList <$> get h

encodeFile :: Binary a => FilePath -> a -> IO ()
encodeFile file a = withBinaryFile file WriteMode $ \h -> put h a

decodeFile :: Binary a => FilePath -> IO a
decodeFile file = withBinaryFile file ReadMode $ \h -> get h