Z-MessagePack-0.1.0.0: Z/Data/MessagePack/Value.hs
{- |
Module : Z.Data.MessagePack.Value
Description : MessagePack object definition and parser
Copyright : (c) Hideyuki Tanaka 2009-2015
, (c) Dong Han 2020
License : BSD3
-}
module Z.Data.MessagePack.Value(
-- * MessagePack Value
Value(..)
-- * parse into Message Value
, parseValue
, parseValue'
, parseValueChunks
, parseValueChunks'
-- * Value Parsers
, value
) where
import Control.DeepSeq
import Control.Monad
import Data.Bits
import Data.Int
import Data.Word
import GHC.Generics (Generic)
import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
import qualified Test.QuickCheck.Gen as Gen
import Prelude hiding (map)
import qualified Z.Data.Text as T
import qualified Z.Data.Parser as P
import qualified Z.Data.Vector as V
-- | Representation of MessagePack data.
data Value
= Bool !Bool -- ^ true or false
| Int {-# UNPACK #-} !Int64 -- ^ an integer
| Float {-# UNPACK #-} !Float -- ^ a floating point number
| Double {-# UNPACK #-} !Double -- ^ a floating point number
| Str {-# UNPACK #-} !T.Text -- ^ a UTF-8 string
| Bin {-# UNPACK #-} !V.Bytes -- ^ a byte array
| Array {-# UNPACK #-} !(V.Vector Value) -- ^ a sequence of objects
| Map {-# UNPACK #-} !(V.Vector (Value, Value)) -- ^ key-value pairs of objects
| Ext {-# UNPACK #-} !Word8 -- ^ type tag
{-# UNPACK #-} !V.Bytes -- ^ data payload
| Nil -- ^ nil
deriving (Show, Eq, Ord, Generic)
deriving anyclass T.Print
instance NFData Value where
rnf obj = case obj of
Array a -> rnf a
Map m -> rnf m
_ -> ()
instance Arbitrary Value where
arbitrary = Gen.sized $ \n -> Gen.oneof
[ Bool <$> arbitrary
, Int <$> negatives
, Float <$> arbitrary
, Double <$> arbitrary
, Str <$> arbitrary
, Bin <$> arbitrary
, Array <$> Gen.resize (n `div` 2) arbitrary
, Map <$> Gen.resize (n `div` 4) arbitrary
, Ext <$> arbitrary <*> arbitrary
, pure Nil
]
where negatives = Gen.choose (minBound, -1)
value :: P.Parser Value
{-# INLINABLE value #-}
value = do
tag <- P.anyWord8
case tag of
-- Nil
0xC0 -> return Nil
-- Bool
0xC2 -> return (Bool False)
0xC3 -> return (Bool True)
-- Integer
c | c .&. 0x80 == 0x00 -> return (Int (fromIntegral c))
| c .&. 0xE0 == 0xE0 -> return (Int (fromIntegral (fromIntegral c :: Int8)))
0xCC -> Int . fromIntegral <$> P.anyWord8
0xCD -> Int . fromIntegral <$> P.decodePrimBE @Word16
0xCE -> Int . fromIntegral <$> P.decodePrimBE @Word32
0xCF -> Int . fromIntegral <$> P.decodePrimBE @Word64
0xD0 -> Int . fromIntegral <$> P.decodePrim @Int8
0xD1 -> Int . fromIntegral <$> P.decodePrimBE @Int16
0xD2 -> Int . fromIntegral <$> P.decodePrimBE @Int32
0xD3 -> Int . fromIntegral <$> P.decodePrimBE @Int64
-- Float
0xCA -> Float <$> P.decodePrimBE @Float
-- Double
0xCB -> Double <$> P.decodePrimBE @Double
-- String
t | t .&. 0xE0 == 0xA0 -> str (t .&. 0x1F)
0xD9 -> str =<< P.anyWord8
0xDA -> str =<< P.decodePrimBE @Word16
0xDB -> str =<< P.decodePrimBE @Word32
-- Binary
0xC4 -> bin =<< P.anyWord8
0xC5 -> bin =<< P.decodePrimBE @Word16
0xC6 -> bin =<< P.decodePrimBE @Word32
-- Array
t | t .&. 0xF0 == 0x90 -> array (t .&. 0x0F)
0xDC -> array =<< P.decodePrimBE @Word16
0xDD -> array =<< P.decodePrimBE @Word32
-- Map
t | t .&. 0xF0 == 0x80 -> map (t .&. 0x0F)
0xDE -> map =<< P.decodePrimBE @Word16
0xDF -> map =<< P.decodePrimBE @Word32
-- Ext
0xD4 -> ext (1 :: Int)
0xD5 -> ext (2 :: Int)
0xD6 -> ext (4 :: Int)
0xD7 -> ext (8 :: Int)
0xD8 -> ext (16 :: Int)
0xC7 -> ext =<< P.anyWord8
0xC8 -> ext =<< P.decodePrimBE @Word16
0xC9 -> ext =<< P.decodePrimBE @Word32
-- impossible
x -> P.fail' ("Z.Data.MessagePack: unknown tag " <> T.toText x)
where
str !l = do
bs <- P.take (fromIntegral l)
case T.validateMaybe bs of
Just t -> return (Str t)
_ -> P.fail' "Z.Data.MessagePack: illegal UTF8 Bytes"
bin !l = Bin <$> P.take (fromIntegral l)
array !l = Array . V.packN (fromIntegral l) <$> replicateM (fromIntegral l) value
map !l = Map . V.packN (fromIntegral l) <$> replicateM (fromIntegral l) ((,) <$> value <*> value)
ext !l = Ext <$> P.decodePrim <*> P.take (fromIntegral l)
-- | Parse 'Value' without consuming trailing bytes.
parseValue :: V.Bytes -> (V.Bytes, Either P.ParseError Value)
{-# INLINE parseValue #-}
parseValue = P.parse value
-- | Parse 'Value', if there're bytes left, parsing will fail.
parseValue' :: V.Bytes -> Either P.ParseError Value
{-# INLINE parseValue' #-}
parseValue' = P.parse' (value <* P.endOfInput)
-- | Increamental parse 'Value' without consuming trailing bytes.
parseValueChunks :: Monad m => m V.Bytes -> V.Bytes -> m (V.Bytes, Either P.ParseError Value)
{-# INLINE parseValueChunks #-}
parseValueChunks = P.parseChunks value
-- | Increamental parse 'Value', if there're bytes left, parsing will fail.
parseValueChunks' :: Monad m => m V.Bytes -> V.Bytes -> m (Either P.ParseError Value)
{-# INLINE parseValueChunks' #-}
parseValueChunks' mi inp = snd <$> P.parseChunks (value <* P.endOfInput) mi inp