{-# LANGUAGE CPP, ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}

-- | Efficient 'Data.Serialize' instance for 'Data.Vector.Storable'
-- vectors. The serialized format is an 'Int64' representing the
-- length of the 'Vector', followed by the raw bytes.
module Data.Vector.Storable.Serialize () where

import Control.Monad (when)

import qualified Data.ByteString.Internal as BS
import Data.Int (Int64)
import Data.Serialize (Get, getBytes, putByteString, Serialize(..))
import Data.Vector.Storable ( unsafeFromForeignPtr0
                            , unsafeToForeignPtr0
                            , Vector)
import Data.Vector.Storable.Internal (updPtr)

import Foreign.ForeignPtr (castForeignPtr)
import Foreign.Marshal.Array (advancePtr)
import Foreign.Storable (Storable, sizeOf)

instance forall a. Storable a => Serialize (Vector a) where
  get = do 
    len64 <- (get :: Get Int64)
    when (len64 > fromIntegral (maxBound :: Int)) $
      fail "Host can't deserialize a Vector longer than (maxBound :: Int)"
    let len    = fromIntegral len64
        nbytes = len * sizeOf (undefined :: a)
    bs <- getBytes nbytes
    let (fp, off, _)    = BS.toForeignPtr bs
        fp' | off /= 0  = updPtr (`advancePtr` off) fp
            | otherwise = fp
    return $ unsafeFromForeignPtr0 (castForeignPtr fp') len

  put v = do
    let (fp, len) = unsafeToForeignPtr0 v
        nbytes    = (len * sizeOf (undefined :: a))
        bs        = BS.fromForeignPtr (castForeignPtr fp) 0 nbytes
    put ((fromIntegral len) :: Int64)
    putByteString bs