{-# Language BangPatterns #-}
module Text.ProtocolBuffers.WireMessage
(
messageSize,messagePut,messageGet,messagePutM,messageGetM
, messageWithLengthSize,messageWithLengthPut,messageWithLengthGet,messageWithLengthPutM,messageWithLengthGetM
, messageAsFieldSize,messageAsFieldPutM,messageAsFieldGetM
, Put,PutM,Get,runPut,runPutM,runGet,runGetOnLazy,getFromBS
, Wire(..)
, size'WireTag,size'WireSize,toWireType,toWireTag,toPackedWireTag,mkWireTag
, prependMessageSize,putSize,putVarUInt,getVarInt,putLazyByteString,splitWireTag,fieldIdOf
, wireSizeReq,wireSizeOpt,wireSizeRep,wireSizePacked
, wirePutReq,wirePutOpt,wirePutRep,wirePutPacked
, wirePutReqWithSize,wirePutOptWithSize,wirePutRepWithSize,wirePutPackedWithSize
, sequencePutWithSize
, wireSizeErr,wirePutErr,wireGetErr
, getMessageWith,getBareMessageWith,wireGetEnum,wireGetPackedEnum
, unknownField,unknown,wireGetFromWire
, castWord64ToDouble,castWord32ToFloat,castDoubleToWord64,castFloatToWord32
, zzEncode64,zzEncode32,zzDecode64,zzDecode32
) where
import Control.Monad(when,foldM)
import Control.Monad.Error.Class(throwError)
import Control.Monad.ST
import Data.Array.ST(newArray,readArray)
import Data.Array.Unsafe(castSTUArray)
import Data.Bits (Bits(..))
import qualified Data.ByteString.Lazy as BS (length)
import qualified Data.Foldable as F(foldl', Foldable)
import Data.Maybe(fromMaybe)
import Data.Sequence ((|>))
import qualified Data.Sequence as Seq(length,empty)
import qualified Data.Set as Set(delete,null)
import Data.Typeable (Typeable,typeOf)
import Data.Binary.Put (Put,PutM,runPutM,runPut,putWord8,putWord32le,putWord64le,putLazyByteString)
import Text.ProtocolBuffers.Basic
import Text.ProtocolBuffers.Get as Get (Result(..),Get,runGet,runGetAll,bytesRead,isReallyEmpty,decode7unrolled
,spanOf,skip,lookAhead,highBitRun
,getWord32le,getWord64le,getLazyByteString)
import Text.ProtocolBuffers.Reflections(ReflectDescriptor(reflectDescriptorInfo,getMessageInfo)
,DescriptorInfo(..),GetMessageInfo(..))
trace :: a -> b -> b
trace _ = id
messageSize :: (ReflectDescriptor msg,Wire msg) => msg -> WireSize
messageSize msg = wireSize 10 msg
messageWithLengthSize :: (ReflectDescriptor msg,Wire msg) => msg -> WireSize
messageWithLengthSize msg = wireSize 11 msg
messageAsFieldSize :: (ReflectDescriptor msg,Wire msg) => FieldId -> msg -> WireSize
messageAsFieldSize fi msg = let headerSize = size'WireTag (toWireTag fi 11)
in headerSize + messageWithLengthSize msg
messagePut :: (ReflectDescriptor msg, Wire msg) => msg -> ByteString
messagePut msg = runPut (messagePutM msg)
messageWithLengthPut :: (ReflectDescriptor msg, Wire msg) => msg -> ByteString
messageWithLengthPut msg = runPut (messageWithLengthPutM msg)
messagePutM :: (ReflectDescriptor msg, Wire msg) => msg -> Put
messagePutM msg = wirePut 10 msg
messageWithLengthPutM :: (ReflectDescriptor msg, Wire msg) => msg -> Put
messageWithLengthPutM msg = wirePut 11 msg
messageAsFieldPutM :: (ReflectDescriptor msg, Wire msg) => FieldId -> msg -> Put
messageAsFieldPutM fi msg = let wireTag = toWireTag fi 11
in wirePutReq wireTag 11 msg
messageGet :: (ReflectDescriptor msg, Wire msg) => ByteString -> Either String (msg,ByteString)
messageGet bs = runGetOnLazy messageGetM bs
messageWithLengthGet :: (ReflectDescriptor msg, Wire msg) => ByteString -> Either String (msg,ByteString)
messageWithLengthGet bs = runGetOnLazy messageWithLengthGetM bs
messageGetM :: (ReflectDescriptor msg, Wire msg) => Get msg
messageGetM = wireGet 10
messageWithLengthGetM :: (ReflectDescriptor msg, Wire msg) => Get msg
messageWithLengthGetM = wireGet 11
messageAsFieldGetM :: (ReflectDescriptor msg, Wire msg) => Get (FieldId,msg)
messageAsFieldGetM = do
wireTag <- fmap WireTag getVarInt
let (fieldId,wireType) = splitWireTag wireTag
when (wireType /= 2) (throwError $ "messageAsFieldGetM: wireType was not 2 "++show (fieldId,wireType))
msg <- wireGet 11
return (fieldId,msg)
getFromBS :: Get r -> ByteString -> r
getFromBS parser bs = case runGetOnLazy parser bs of
Left msg -> error msg
Right (r,_) -> r
runGetOnLazy :: Get r -> ByteString -> Either String (r,ByteString)
runGetOnLazy parser bs = resolve (runGetAll parser bs)
where resolve :: Result r -> Either String (r,ByteString)
resolve (Failed i s) = Left ("Failed at "++show i++" : "++s)
resolve (Finished bsOut _i r) = Right (r,bsOut)
resolve (Partial op) = resolve (op Nothing)
prependMessageSize :: WireSize -> WireSize
prependMessageSize n = n + size'WireSize n
{-# INLINE sequencePutWithSize #-}
sequencePutWithSize :: F.Foldable f => f (PutM WireSize) -> PutM WireSize
sequencePutWithSize =
let combine size act =
do size2 <- act
return $! size + size2
in foldM combine 0
{-# INLINE wirePutReqWithSize #-}
wirePutReqWithSize :: Wire v => WireTag -> FieldType -> v -> PutM WireSize
wirePutReqWithSize wireTag fieldType v =
let startTag = getWireTag wireTag
endTag = succ startTag
putTag tag = putVarUInt tag >> return (size'Word32 tag)
putAct = wirePutWithSize fieldType v
in case fieldType of
10 -> sequencePutWithSize [putTag startTag, putAct, putTag endTag]
_ -> sequencePutWithSize [putTag startTag, putAct]
{-# INLINE wirePutOptWithSize #-}
wirePutOptWithSize :: Wire v => WireTag -> FieldType -> Maybe v -> PutM WireSize
wirePutOptWithSize _wireTag _fieldType Nothing = return 0
wirePutOptWithSize wireTag fieldType (Just v) = wirePutReqWithSize wireTag fieldType v
{-# INLINE wirePutRepWithSize #-}
wirePutRepWithSize :: Wire v => WireTag -> FieldType -> Seq v -> PutM WireSize
wirePutRepWithSize wireTag fieldType vs =
sequencePutWithSize $ fmap (wirePutReqWithSize wireTag fieldType) vs
{-# INLINE wirePutPackedWithSize #-}
wirePutPackedWithSize :: Wire v => WireTag -> FieldType -> Seq v -> PutM WireSize
wirePutPackedWithSize wireTag fieldType vs =
let actInner = sequencePutWithSize $ fmap (wirePutWithSize fieldType) vs
(size, _) = runPutM actInner
tagSize = size'WireTag wireTag
putTag tag = putVarUInt (getWireTag tag) >> return tagSize
in sequencePutWithSize [putTag wireTag, putSize size>>return (size'WireSize size), actInner]
{-# INLINE wirePutReq #-}
wirePutReq :: Wire v => WireTag -> FieldType -> v -> Put
wirePutReq wireTag fieldType v = wirePutReqWithSize wireTag fieldType v >> return ()
{-# INLINE wirePutOpt #-}
wirePutOpt :: Wire v => WireTag -> FieldType -> Maybe v -> Put
wirePutOpt wireTag fieldType v = wirePutOptWithSize wireTag fieldType v >> return ()
{-# INLINE wirePutRep #-}
wirePutRep :: Wire v => WireTag -> FieldType -> Seq v -> Put
wirePutRep wireTag fieldType vs = wirePutRepWithSize wireTag fieldType vs >> return ()
{-# INLINE wirePutPacked #-}
wirePutPacked :: Wire v => WireTag -> FieldType -> Seq v -> Put
wirePutPacked wireTag fieldType vs = wirePutPackedWithSize wireTag fieldType vs >> return ()
{-# INLINE wireSizeReq #-}
wireSizeReq :: Wire v => Int64 -> FieldType -> v -> Int64
wireSizeReq tagSize 10 v = tagSize + wireSize 10 v + tagSize
wireSizeReq tagSize fieldType v = tagSize + wireSize fieldType v
{-# INLINE wireSizeOpt #-}
wireSizeOpt :: Wire v => Int64 -> FieldType -> Maybe v -> Int64
wireSizeOpt _tagSize _i Nothing = 0
wireSizeOpt tagSize i (Just v) = wireSizeReq tagSize i v
{-# INLINE wireSizeRep #-}
wireSizeRep :: Wire v => Int64 -> FieldType -> Seq v -> Int64
wireSizeRep tagSize i vs = F.foldl' (\n v -> n + wireSizeReq tagSize i v) 0 vs
{-# INLINE wireSizePacked #-}
wireSizePacked :: Wire v => Int64 -> FieldType -> Seq v -> Int64
wireSizePacked tagSize i vs = tagSize + prependMessageSize (F.foldl' (\n v -> n + wireSize i v) 0 vs)
{-# INLINE putSize #-}
putSize :: WireSize -> Put
putSize = putVarUInt
toPackedWireTag :: FieldId -> WireTag
toPackedWireTag fieldId = mkWireTag fieldId 2
toWireTag :: FieldId -> FieldType -> WireTag
toWireTag fieldId fieldType
= mkWireTag fieldId (toWireType fieldType)
mkWireTag :: FieldId -> WireType -> WireTag
mkWireTag fieldId wireType
= ((fromIntegral . getFieldId $ fieldId) `shiftL` 3) .|. (fromIntegral . getWireType $ wireType)
splitWireTag :: WireTag -> (FieldId,WireType)
splitWireTag (WireTag wireTag) = ( FieldId . fromIntegral $ wireTag `shiftR` 3
, WireType . fromIntegral $ wireTag .&. 7 )
fieldIdOf :: WireTag -> FieldId
fieldIdOf = fst . splitWireTag
{-# INLINE wireGetPackedEnum #-}
wireGetPackedEnum :: (Typeable e,Enum e) => (Int -> Maybe e) -> Get (Seq e)
wireGetPackedEnum toMaybe'Enum = do
packedLength <- getVarInt
start <- bytesRead
let stop = packedLength+start
next !soFar = do
here <- bytesRead
case compare stop here of
EQ -> return soFar
LT -> tooMuchData packedLength soFar start here
GT -> do
value <- wireGetEnum toMaybe'Enum
seq value $ next (soFar |> value)
next Seq.empty
where
Just e = undefined `asTypeOf` (toMaybe'Enum undefined)
tooMuchData packedLength soFar start here =
throwError ("Text.ProtocolBuffers.WireMessage.wireGetPackedEnum: overran expected length."
++ "\n The type and count of values so far is " ++ show (typeOf (undefined `asTypeOf` e),Seq.length soFar)
++ "\n at (packedLength,start,here) == " ++ show (packedLength,start,here))
{-# INLINE genericPacked #-}
genericPacked :: Wire a => FieldType -> Get (Seq a)
genericPacked ft = do
packedLength <- getVarInt
start <- bytesRead
let stop = packedLength+start
next !soFar = do
here <- bytesRead
case compare stop here of
EQ -> return soFar
LT -> tooMuchData packedLength soFar start here
GT -> do
value <- wireGet ft
seq value $! next $! soFar |> value
next Seq.empty
where
tooMuchData packedLength soFar start here =
throwError ("Text.ProtocolBuffers.WireMessage.genericPacked: overran expected length."
++ "\n The FieldType and count of values so far are " ++ show (ft,Seq.length soFar)
++ "\n at (packedLength,start,here) == " ++ show (packedLength,start,here))
getMessageWith :: (Default message, ReflectDescriptor message)
=> (WireTag -> message -> Get message)
-> Get message
getMessageWith updater = do
messageLength <- getVarInt
start <- bytesRead
let stop = messageLength+start
go reqs !message | Set.null reqs = go' message
| otherwise = do
here <- bytesRead
case compare stop here of
EQ -> notEnoughData messageLength start
LT -> tooMuchData messageLength start here
GT -> do
wireTag <- fmap WireTag getVarInt
let
reqs' = Set.delete wireTag reqs
updater wireTag message >>= go reqs'
go' !message = do
here <- bytesRead
case compare stop here of
EQ -> return message
LT -> tooMuchData messageLength start here
GT -> do
wireTag <- fmap WireTag getVarInt
updater wireTag message >>= go'
go required initialMessage
where
initialMessage = defaultValue
(GetMessageInfo {requiredTags=required}) = getMessageInfo initialMessage
notEnoughData messageLength start =
throwError ("Text.ProtocolBuffers.WireMessage.getMessageWith: Required fields missing when processing "
++ (show . descName . reflectDescriptorInfo $ initialMessage)
++ "\n at (messageLength,start) == " ++ show (messageLength,start))
tooMuchData messageLength start here =
throwError ("Text.ProtocolBuffers.WireMessage.getMessageWith: overran expected length when processing"
++ (show . descName . reflectDescriptorInfo $ initialMessage)
++ "\n at (messageLength,start,here) == " ++ show (messageLength,start,here))
getBareMessageWith :: (Default message, ReflectDescriptor message)
=> (WireTag -> message -> Get message)
-> Get message
getBareMessageWith updater = go required initialMessage
where
go reqs !message | Set.null reqs = go' message
| otherwise = do
done <- isReallyEmpty
if done then notEnoughData
else do
wireTag <- fmap WireTag getVarInt
let (_fieldId,wireType) = splitWireTag wireTag
if wireType == 4 then notEnoughData
else let reqs' = Set.delete wireTag reqs
in updater wireTag message >>= go reqs'
go' !message = do
done <- isReallyEmpty
if done then return message
else do
wireTag <- fmap WireTag getVarInt
let (_fieldId,wireType) = splitWireTag wireTag
if wireType == 4 then return message
else updater wireTag message >>= go'
initialMessage = defaultValue
(GetMessageInfo {requiredTags=required}) = getMessageInfo initialMessage
notEnoughData = throwError ("Text.ProtocolBuffers.WireMessage.getBareMessageWith: Required fields missing when processing "
++ (show . descName . reflectDescriptorInfo $ initialMessage))
unknownField :: Typeable a => a -> FieldId -> Get a
unknownField msg fieldId = do
here <- bytesRead
throwError ("Impossible? Text.ProtocolBuffers.WireMessage.unknownField"
++"\n Updater for "++show (typeOf msg)++" claims there is an unknown field id on wire: "++show fieldId
++"\n at a position just before byte location "++show here)
unknown :: (Typeable a,ReflectDescriptor a) => FieldId -> WireType -> a -> Get a
unknown fieldId wireType initialMessage = do
here <- bytesRead
throwError ("Text.ProtocolBuffers.WireMessage.unknown: Unknown field found or failure parsing field (e.g. unexpected Enum value):"
++ "\n (message type name,field id number,wire type code,bytes read) == "
++ show (typeOf initialMessage,fieldId,wireType,here)
++ "\n when processing "
++ (show . descName . reflectDescriptorInfo $ initialMessage))
{-# INLINE castWord32ToFloat #-}
castWord32ToFloat :: Word32 -> Float
castWord32ToFloat x = runST (newArray (0::Int,0) x >>= castSTUArray >>= flip readArray 0)
{-# INLINE castFloatToWord32 #-}
castFloatToWord32 :: Float -> Word32
castFloatToWord32 x = runST (newArray (0::Int,0) x >>= castSTUArray >>= flip readArray 0)
{-# INLINE castWord64ToDouble #-}
castWord64ToDouble :: Word64 -> Double
castWord64ToDouble x = runST (newArray (0::Int,0) x >>= castSTUArray >>= flip readArray 0)
{-# INLINE castDoubleToWord64 #-}
castDoubleToWord64 :: Double -> Word64
castDoubleToWord64 x = runST (newArray (0::Int,0) x >>= castSTUArray >>= flip readArray 0)
wireSizeErr :: Typeable a => FieldType -> a -> WireSize
wireSizeErr ft x = error $ concat [ "Impossible? wireSize field type mismatch error: Field type number ", show ft
, " does not match internal type ", show (typeOf x) ]
wirePutErr :: Typeable a => FieldType -> a -> PutM b
wirePutErr ft x = error $ concat [ "Impossible? wirePut field type mismatch error: Field type number ", show ft
, " does not match internal type ", show (typeOf x) ]
wireGetErr :: Typeable a => FieldType -> Get a
wireGetErr ft = answer where
answer = throwError $ concat [ "Impossible? wireGet field type mismatch error: Field type number ", show ft
, " does not match internal type ", show (typeOf (undefined `asTypeOf` typeHack answer)) ]
typeHack :: Get a -> a
typeHack = undefined
class Wire b where
{-# MINIMAL wireGet, wireSize, (wirePut | wirePutWithSize) #-}
wireSize :: FieldType -> b -> WireSize
{-# INLINE wirePut #-}
wirePut :: FieldType -> b -> Put
wirePut ft x = wirePutWithSize ft x >> return ()
{-# INLINE wirePutWithSize #-}
wirePutWithSize :: FieldType -> b -> PutM WireSize
wirePutWithSize ft x = wirePut ft x >> return (wireSize ft x)
wireGet :: FieldType -> Get b
{-# INLINE wireGetPacked #-}
wireGetPacked :: FieldType -> Get (Seq b)
wireGetPacked ft = throwError ("Text.ProtocolBuffers.ProtoCompile.Basic: wireGetPacked default:"
++ "\n There is no way to get a packed FieldType of "++show ft
++ ".\n Either there is a bug in this library or the wire format is has been updated.")
instance Wire Double where
{-# INLINE wireSize #-}
wireSize 1 _ = 8
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 1 x = putWord64le (castDoubleToWord64 x)
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 1 = fmap castWord64ToDouble getWord64le
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 1 = genericPacked 1
wireGetPacked ft = wireGetErr ft
instance Wire Float where
{-# INLINE wireSize #-}
wireSize 2 _ = 4
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 2 x = putWord32le (castFloatToWord32 x)
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 2 = fmap castWord32ToFloat getWord32le
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 2 = genericPacked 2
wireGetPacked ft = wireGetErr ft
instance Wire Int64 where
{-# INLINE wireSize #-}
wireSize 3 x = size'Int64 x
wireSize 18 x = size'Word64 (zzEncode64 x)
wireSize 16 _ = 8
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 3 x = putVarSInt x
wirePut 18 x = putVarUInt (zzEncode64 x)
wirePut 16 x = putWord64le (fromIntegral x)
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 3 = getVarInt
wireGet 18 = fmap zzDecode64 getVarInt
wireGet 16 = fmap fromIntegral getWord64le
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 3 = genericPacked 3
wireGetPacked 18 = genericPacked 18
wireGetPacked 16 = genericPacked 16
wireGetPacked ft = wireGetErr ft
instance Wire Int32 where
{-# INLINE wireSize #-}
wireSize 5 x = size'Int32 x
wireSize 17 x = size'Word32 (zzEncode32 x)
wireSize 15 _ = 4
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 5 x = putVarSInt x
wirePut 17 x = putVarUInt (zzEncode32 x)
wirePut 15 x = putWord32le (fromIntegral x)
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 5 = getVarInt
wireGet 17 = fmap zzDecode32 getVarInt
wireGet 15 = fmap fromIntegral getWord32le
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 5 = genericPacked 5
wireGetPacked 17 = genericPacked 17
wireGetPacked 15 = genericPacked 15
wireGetPacked ft = wireGetErr ft
instance Wire Word64 where
{-# INLINE wireSize #-}
wireSize 4 x = size'Word64 x
wireSize 6 _ = 8
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 4 x = putVarUInt x
wirePut 6 x = putWord64le x
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 6 = getWord64le
wireGet 4 = getVarInt
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 6 = genericPacked 6
wireGetPacked 4 = genericPacked 4
wireGetPacked ft = wireGetErr ft
instance Wire Word32 where
{-# INLINE wireSize #-}
wireSize 13 x = size'Word32 x
wireSize 7 _ = 4
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 13 x = putVarUInt x
wirePut 7 x = putWord32le x
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 13 = getVarInt
wireGet 7 = getWord32le
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 13 = genericPacked 13
wireGetPacked 7 = genericPacked 7
wireGetPacked ft = wireGetErr ft
instance Wire Bool where
{-# INLINE wireSize #-}
wireSize 8 _ = 1
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 8 False = putWord8 0
wirePut 8 True = putWord8 1
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 8 = do
x <- getVarInt :: Get Int32
case x of
0 -> return False
_ -> return True
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 8 = genericPacked 8
wireGetPacked ft = wireGetErr ft
instance Wire Utf8 where
{-# INLINE wireSize #-}
wireSize 9 x = prependMessageSize $ BS.length (utf8 x)
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 9 x = putVarUInt (BS.length (utf8 x)) >> putLazyByteString (utf8 x)
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 9 = getVarInt >>= getLazyByteString >>= verifyUtf8
wireGet ft = wireGetErr ft
instance Wire ByteString where
{-# INLINE wireSize #-}
wireSize 12 x = prependMessageSize $ BS.length x
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 12 x = putVarUInt (BS.length x) >> putLazyByteString x
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 12 = getVarInt >>= getLazyByteString
wireGet ft = wireGetErr ft
instance Wire Int where
{-# INLINE wireSize #-}
wireSize 14 x = size'Int x
wireSize ft x = wireSizeErr ft x
{-# INLINE wirePut #-}
wirePut 14 x = putVarSInt x
wirePut ft x = wirePutErr ft x
{-# INLINE wireGet #-}
wireGet 14 = getVarInt
wireGet ft = wireGetErr ft
{-# INLINE wireGetPacked #-}
wireGetPacked 14 = genericPacked 14
wireGetPacked ft = wireGetErr ft
{-# INLINE verifyUtf8 #-}
verifyUtf8 :: ByteString -> Get Utf8
verifyUtf8 bs = case isValidUTF8 bs of
Nothing -> return (Utf8 bs)
Just i -> throwError $ "Text.ProtocolBuffers.WireMessage.verifyUtf8: ByteString is not valid utf8 at position "++show i
{-# INLINE wireGetEnum #-}
wireGetEnum :: (Typeable e, Enum e) => (Int -> Maybe e) -> Get e
wireGetEnum toMaybe'Enum = do
int <- wireGet 14
case toMaybe'Enum int of
Just !v -> return v
Nothing -> throwError (msg ++ show int)
where msg = "Bad wireGet of Enum "++show (typeOf (undefined `asTypeOf` typeHack toMaybe'Enum))++", unrecognized Int value is "
typeHack :: (Int -> Maybe e) -> e
typeHack f = fromMaybe undefined (f undefined)
size'WireTag :: WireTag -> Int64
size'WireTag = size'Word32 . getWireTag
size'Word32 :: Word32 -> Int64
size'Word32 b | b <= 0x7F = 1
| b <= 0x3FFF = 2
| b <= 0x1FFFFF = 3
| b <= 0xFFFFFFF = 4
| otherwise = 5
size'Int32 :: Int32 -> Int64
size'Int32 b | b < 0 = 10
| b <= 0x7F = 1
| b <= 0x3FFF = 2
| b <= 0x1FFFFF = 3
| b <= 0xFFFFFFF = 4
| otherwise = 5
size'Word64 :: Word64 -> Int64
size'Word64 b | b <= 0x7F = 1
| b <= 0x3FFF = 2
| b <= 0x1FFFFF = 3
| b <= 0xFFFFFFF = 4
| b <= 0X7FFFFFFFF = 5
| b <= 0x3FFFFFFFFFF = 6
| b <= 0x1FFFFFFFFFFFF = 7
| b <= 0xFFFFFFFFFFFFFF = 8
| b <= 0x7FFFFFFFFFFFFFFF = 9
| otherwise = 10
size'Int :: Int -> Int64
size'Int b | b < 0 = 10
| b <= 0x7F = 1
| b <= 0x3FFF = 2
| b <= 0x1FFFFF = 3
| b <= 0xFFFFFFF = 4
| b <= 0x7FFFFFFF = 5
| b <= 0x7FFFFFFFF = 5
| b <= 0x3FFFFFFFFFF = 6
| b <= 0x1FFFFFFFFFFFF = 7
| b <= 0xFFFFFFFFFFFFFF = 8
| otherwise = 9
size'Int64,size'WireSize :: Int64 -> Int64
size'WireSize = size'Int64
size'Int64 b | b < 0 = 10
| b <= 0x7F = 1
| b <= 0x3FFF = 2
| b <= 0x1FFFFF = 3
| b <= 0xFFFFFFF = 4
| b <= 0x7FFFFFFFF = 5
| b <= 0x3FFFFFFFFFF = 6
| b <= 0x1FFFFFFFFFFFF = 7
| b <= 0xFFFFFFFFFFFFFF = 8
| otherwise = 9
zzEncode32 :: Int32 -> Word32
zzEncode32 x = fromIntegral ((x `shiftL` 1) `xor` (x `shiftR` 31))
zzEncode64 :: Int64 -> Word64
zzEncode64 x = fromIntegral ((x `shiftL` 1) `xor` (x `shiftR` 63))
zzDecode32 :: Word32 -> Int32
zzDecode32 w = (fromIntegral (w `shiftR` 1)) `xor` (negate (fromIntegral (w .&. 1)))
zzDecode64 :: Word64 -> Int64
zzDecode64 w = (fromIntegral (w `shiftR` 1)) `xor` (negate (fromIntegral (w .&. 1)))
getVarInt :: (Show a, Integral a, Bits a) => Get a
{-# INLINE getVarInt #-}
getVarInt = do
a <- decode7unrolled
trace ("getVarInt: "++show a) $ return a
{-# INLINE putVarSInt #-}
putVarSInt :: (Integral a, Bits a) => a -> Put
putVarSInt bIn =
case compare bIn 0 of
LT -> let b :: Int64
b = fromIntegral bIn
len :: Int
len = 10
last'Mask = 1
go !i 1 = putWord8 (fromIntegral (i .&. last'Mask))
go !i n = putWord8 (fromIntegral (i .&. 0x7F) .|. 0x80) >> go (i `shiftR` 7) (pred n)
in go b len
EQ -> putWord8 0
GT -> putVarUInt bIn
{-# INLINE putVarUInt #-}
putVarUInt :: (Integral a, Bits a) => a -> Put
putVarUInt i | i < 0x80 = putWord8 (fromIntegral i)
| otherwise = putWord8 (fromIntegral (i .&. 0x7F) .|. 0x80) >> putVarUInt (i `shiftR` 7)
wireGetFromWire :: FieldId -> WireType -> Get ByteString
wireGetFromWire fi wt = getLazyByteString =<< calcLen where
calcLen = case wt of
0 -> highBitRun
1 -> return 8
2 -> lookAhead $ do
here <- bytesRead
len <- getVarInt
there <- bytesRead
return ((there-here)+len)
3 -> lenOf (skipGroup fi)
4 -> throwError $ "Cannot wireGetFromWire with wireType of STOP_GROUP: "++show (fi,wt)
5 -> return 4
wtf -> throwError $ "Invalid wire type (expected 0,1,2,3,or 5) found: "++show (fi,wtf)
lenOf g = do here <- bytesRead
there <- lookAhead (g >> bytesRead)
trace (":wireGetFromWire.lenOf: "++show ((fi,wt),(here,there,there-here))) $ return (there-here)
skipGroup :: FieldId -> Get ()
skipGroup start_fi = go where
go = do
(fieldId,wireType) <- fmap (splitWireTag . WireTag) getVarInt
case wireType of
0 -> spanOf (>=128) >> skip 1 >> go
1 -> skip 8 >> go
2 -> getVarInt >>= skip >> go
3 -> skipGroup fieldId >> go
4 | start_fi /= fieldId -> throwError $ "skipGroup failed, fieldId mismatch bewteen START_GROUP and STOP_GROUP: "++show (start_fi,(fieldId,wireType))
| otherwise -> return ()
5 -> skip 4 >> go
wtf -> throwError $ "Invalid wire type (expected 0,1,2,3,4,or 5) found: "++show (fieldId,wtf)
toWireType :: FieldType -> WireType
toWireType 1 = 1
toWireType 2 = 5
toWireType 3 = 0
toWireType 4 = 0
toWireType 5 = 0
toWireType 6 = 1
toWireType 7 = 5
toWireType 8 = 0
toWireType 9 = 2
toWireType 10 = 3
toWireType 11 = 2
toWireType 12 = 2
toWireType 13 = 0
toWireType 14 = 0
toWireType 15 = 5
toWireType 16 = 1
toWireType 17 = 0
toWireType 18 = 0
toWireType x = error $ "Text.ProcolBuffers.Basic.toWireType: Bad FieldType: "++show x