module Data.MessagePack.Pack (
Packable(..),
pack,
) where
import Blaze.ByteString.Builder
import Data.Bits
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.HashMap.Strict as HM
import qualified Data.Map as M
import qualified Data.IntMap as IM
import qualified Data.Monoid as Monoid
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import qualified Data.Vector as V
import Foreign
import qualified System.IO.Unsafe as SIU
import Data.MessagePack.Assoc
import Data.MessagePack.Internal.Utf8
(<>) :: Monoid.Monoid m => m -> m -> m
(<>) = Monoid.mappend
class Packable a where
from :: a -> Builder
pack :: Packable a => a -> BL.ByteString
pack = toLazyByteString . from
instance Packable Int where
from n =
case n of
_ | n >= 0 && n <= 127 ->
fromWord8 $ fromIntegral n
_ | n >= 32 && n <= 1 ->
fromWord8 $ fromIntegral n
_ | n >= 0 && n < 0x100 ->
fromWord8 0xCC <>
fromWord8 (fromIntegral n)
_ | n >= 0 && n < 0x10000 ->
fromWord8 0xCD <>
fromWord16be (fromIntegral n)
_ | n >= 0 && n < 0x100000000 ->
fromWord8 0xCE <>
fromWord32be (fromIntegral n)
_ | n >= 0 ->
fromWord8 0xCF <>
fromWord64be (fromIntegral n)
_ | n >= 0x80 ->
fromWord8 0xD0 <>
fromWord8 (fromIntegral n)
_ | n >= 0x8000 ->
fromWord8 0xD1 <>
fromWord16be (fromIntegral n)
_ | n >= 0x80000000 ->
fromWord8 0xD2 <>
fromWord32be (fromIntegral n)
_ ->
fromWord8 0xD3 <>
fromWord64be (fromIntegral n)
instance Packable () where
from _ =
fromWord8 0xC0
instance Packable Bool where
from True = fromWord8 0xC3
from False = fromWord8 0xC2
instance Packable Float where
from f =
fromWord8 0xCB <>
fromWord32be (cast f)
instance Packable Double where
from d =
fromWord8 0xCB <>
fromWord64be (cast d)
cast :: (Storable a, Storable b) => a -> b
cast v = SIU.unsafePerformIO $ with v $ peek . castPtr
instance Packable String where
from = fromString encodeUtf8 B.length fromByteString
instance Packable B.ByteString where
from = fromString id B.length fromByteString
instance Packable BL.ByteString where
from = fromString id (fromIntegral . BL.length) fromLazyByteString
instance Packable T.Text where
from = fromString T.encodeUtf8 B.length fromByteString
instance Packable TL.Text where
from = fromString TL.encodeUtf8 (fromIntegral . BL.length) fromLazyByteString
fromString :: (s -> t) -> (t -> Int) -> (t -> Builder) -> s -> Builder
fromString cnv lf pf str =
let bs = cnv str in
case lf bs of
len | len <= 31 ->
fromWord8 $ 0xA0 .|. fromIntegral len
len | len < 0x10000 ->
fromWord8 0xDA <>
fromWord16be (fromIntegral len)
len ->
fromWord8 0xDB <>
fromWord32be (fromIntegral len)
<> pf bs
instance Packable a => Packable [a] where
from = fromArray length (Monoid.mconcat . map from)
instance Packable a => Packable (V.Vector a) where
from = fromArray V.length (V.foldl (\a b -> a <> from b) Monoid.mempty)
instance (Packable a1, Packable a2) => Packable (a1, a2) where
from = fromArray (const 2) f where
f (a1, a2) = from a1 <> from a2
instance (Packable a1, Packable a2, Packable a3) => Packable (a1, a2, a3) where
from = fromArray (const 3) f where
f (a1, a2, a3) = from a1 <> from a2 <> from a3
instance (Packable a1, Packable a2, Packable a3, Packable a4) => Packable (a1, a2, a3, a4) where
from = fromArray (const 4) f where
f (a1, a2, a3, a4) = from a1 <> from a2 <> from a3 <> from a4
instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5) => Packable (a1, a2, a3, a4, a5) where
from = fromArray (const 5) f where
f (a1, a2, a3, a4, a5) = from a1 <> from a2 <> from a3 <> from a4 <> from a5
instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6) => Packable (a1, a2, a3, a4, a5, a6) where
from = fromArray (const 6) f where
f (a1, a2, a3, a4, a5, a6) = from a1 <> from a2 <> from a3 <> from a4 <> from a5 <> from a6
instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6, Packable a7) => Packable (a1, a2, a3, a4, a5, a6, a7) where
from = fromArray (const 7) f where
f (a1, a2, a3, a4, a5, a6, a7) = from a1 <> from a2 <> from a3 <> from a4 <> from a5 <> from a6 <> from a7
instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6, Packable a7, Packable a8) => Packable (a1, a2, a3, a4, a5, a6, a7, a8) where
from = fromArray (const 8) f where
f (a1, a2, a3, a4, a5, a6, a7, a8) = from a1 <> from a2 <> from a3 <> from a4 <> from a5 <> from a6 <> from a7 <> from a8
instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6, Packable a7, Packable a8, Packable a9) => Packable (a1, a2, a3, a4, a5, a6, a7, a8, a9) where
from = fromArray (const 9) f where
f (a1, a2, a3, a4, a5, a6, a7, a8, a9) = from a1 <> from a2 <> from a3 <> from a4 <> from a5 <> from a6 <> from a7 <> from a8 <> from a9
fromArray :: (a -> Int) -> (a -> Builder) -> a -> Builder
fromArray lf pf arr = do
case lf arr of
len | len <= 15 ->
fromWord8 $ 0x90 .|. fromIntegral len
len | len < 0x10000 ->
fromWord8 0xDC <>
fromWord16be (fromIntegral len)
len ->
fromWord8 0xDD <>
fromWord32be (fromIntegral len)
<> pf arr
instance (Packable k, Packable v) => Packable (Assoc [(k,v)]) where
from = fromMap length (Monoid.mconcat . map fromPair) . unAssoc
instance (Packable k, Packable v) => Packable (Assoc (V.Vector (k,v))) where
from = fromMap V.length (V.foldl (\a b -> a <> fromPair b) Monoid.mempty) . unAssoc
instance (Packable k, Packable v) => Packable (M.Map k v) where
from = fromMap M.size (Monoid.mconcat . map fromPair . M.toList)
instance Packable v => Packable (IM.IntMap v) where
from = fromMap IM.size (Monoid.mconcat . map fromPair . IM.toList)
instance (Packable k, Packable v) => Packable (HM.HashMap k v) where
from = fromMap HM.size (Monoid.mconcat . map fromPair . HM.toList)
fromPair :: (Packable a, Packable b) => (a, b) -> Builder
fromPair (a, b) = from a <> from b
fromMap :: (a -> Int) -> (a -> Builder) -> a -> Builder
fromMap lf pf m =
case lf m of
len | len <= 15 ->
fromWord8 $ 0x80 .|. fromIntegral len
len | len < 0x10000 ->
fromWord8 0xDE <>
fromWord16be (fromIntegral len)
len ->
fromWord8 0xDF <>
fromWord32be (fromIntegral len)
<> pf m
instance Packable a => Packable (Maybe a) where
from Nothing = from ()
from (Just a) = from a