| {-# LANGUAGE ExistentialQuantification #-} | 
 | {-# LANGUAGE MagicHash #-} | 
 | -- | 
 | -- Licensed to the Apache Software Foundation (ASF) under one | 
 | -- or more contributor license agreements. See the NOTICE file | 
 | -- distributed with this work for additional information | 
 | -- regarding copyright ownership. The ASF licenses this file | 
 | -- to you under the Apache License, Version 2.0 (the | 
 | -- "License"); you may not use this file except in compliance | 
 | -- with the License. You may obtain a copy of the License at | 
 | -- | 
 | --   http://www.apache.org/licenses/LICENSE-2.0 | 
 | -- | 
 | -- Unless required by applicable law or agreed to in writing, | 
 | -- software distributed under the License is distributed on an | 
 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 | -- KIND, either express or implied. See the License for the | 
 | -- specific language governing permissions and limitations | 
 | -- under the License. | 
 | -- | 
 |  | 
 | module Thrift.Protocol.Binary | 
 |     ( module Thrift.Protocol | 
 |     , BinaryProtocol(..) | 
 |     ) where | 
 |  | 
 | import Control.Exception ( throw ) | 
 | import Control.Monad ( liftM ) | 
 |  | 
 | import qualified Data.Binary | 
 | import Data.Bits | 
 | import Data.Int | 
 |  | 
 | import GHC.Exts | 
 | import GHC.Word | 
 |  | 
 | import Thrift.Protocol | 
 | import Thrift.Transport | 
 |  | 
 | import qualified Data.ByteString.Lazy as LBS | 
 | import qualified Data.ByteString.Lazy.Char8 as LBSChar8 | 
 |  | 
 | version_mask :: Int32 | 
 | version_mask = 0xffff0000 | 
 |  | 
 | version_1 :: Int32 | 
 | version_1    = 0x80010000 | 
 |  | 
 | data BinaryProtocol a = Transport a => BinaryProtocol a | 
 |  | 
 |  | 
 | instance Protocol BinaryProtocol where | 
 |     getTransport (BinaryProtocol t) = t | 
 |  | 
 |     writeMessageBegin p (n, t, s) = do | 
 |         writeI32 p (version_1 .|. (fromIntegral $ fromEnum t)) | 
 |         writeString p n | 
 |         writeI32 p s | 
 |     writeMessageEnd _ = return () | 
 |  | 
 |     writeStructBegin _ _ = return () | 
 |     writeStructEnd _ = return () | 
 |     writeFieldBegin p (_, t, i) = writeType p t >> writeI16 p i | 
 |     writeFieldEnd _ = return () | 
 |     writeFieldStop p = writeType p T_STOP | 
 |     writeMapBegin p (k, v, n) = writeType p k >> writeType p v >> writeI32 p n | 
 |     writeMapEnd _ = return () | 
 |     writeListBegin p (t, n) = writeType p t >> writeI32 p n | 
 |     writeListEnd _ = return () | 
 |     writeSetBegin p (t, n) = writeType p t >> writeI32 p n | 
 |     writeSetEnd _ = return () | 
 |  | 
 |     writeBool p b = tWrite (getTransport p) $ LBS.singleton $ toEnum $ if b then 1 else 0 | 
 |     writeByte p b = tWrite (getTransport p) $ Data.Binary.encode b | 
 |     writeI16 p b = tWrite (getTransport p) $ Data.Binary.encode b | 
 |     writeI32 p b = tWrite (getTransport p) $ Data.Binary.encode b | 
 |     writeI64 p b = tWrite (getTransport p) $ Data.Binary.encode b | 
 |     writeDouble p d = writeI64 p (fromIntegral $ floatBits d) | 
 |     writeString p s = writeI32 p (fromIntegral $ length s) >> tWrite (getTransport p) (LBSChar8.pack s) | 
 |     writeBinary p s = writeI32 p (fromIntegral $ LBS.length s) >> tWrite (getTransport p) s | 
 |  | 
 |     readMessageBegin p = do | 
 |         ver <- readI32 p | 
 |         if (ver .&. version_mask /= version_1) | 
 |             then throw $ ProtocolExn PE_BAD_VERSION "Missing version identifier" | 
 |             else do | 
 |               s <- readString p | 
 |               sz <- readI32 p | 
 |               return (s, toEnum $ fromIntegral $ ver .&. 0xFF, sz) | 
 |     readMessageEnd _ = return () | 
 |     readStructBegin _ = return "" | 
 |     readStructEnd _ = return () | 
 |     readFieldBegin p = do | 
 |         t <- readType p | 
 |         n <- if t /= T_STOP then readI16 p else return 0 | 
 |         return ("", t, n) | 
 |     readFieldEnd _ = return () | 
 |     readMapBegin p = do | 
 |         kt <- readType p | 
 |         vt <- readType p | 
 |         n <- readI32 p | 
 |         return (kt, vt, n) | 
 |     readMapEnd _ = return () | 
 |     readListBegin p = do | 
 |         t <- readType p | 
 |         n <- readI32 p | 
 |         return (t, n) | 
 |     readListEnd _ = return () | 
 |     readSetBegin p = do | 
 |         t <- readType p | 
 |         n <- readI32 p | 
 |         return (t, n) | 
 |     readSetEnd _ = return () | 
 |  | 
 |     readBool p = (== 1) `fmap` readByte p | 
 |  | 
 |     readByte p = do | 
 |         bs <- tReadAll (getTransport p) 1 | 
 |         return $ Data.Binary.decode bs | 
 |  | 
 |     readI16 p = do | 
 |         bs <- tReadAll (getTransport p) 2 | 
 |         return $ Data.Binary.decode bs | 
 |  | 
 |     readI32 p = do | 
 |         bs <- tReadAll (getTransport p) 4 | 
 |         return $ Data.Binary.decode bs | 
 |  | 
 |     readI64 p = do | 
 |         bs <- tReadAll (getTransport p) 8 | 
 |         return $ Data.Binary.decode bs | 
 |  | 
 |     readDouble p = do | 
 |         bs <- readI64 p | 
 |         return $ floatOfBits $ fromIntegral bs | 
 |  | 
 |     readString p = do | 
 |         i <- readI32 p | 
 |         LBSChar8.unpack `liftM` tReadAll (getTransport p) (fromIntegral i) | 
 |  | 
 |     readBinary p = do | 
 |         i <- readI32 p | 
 |         tReadAll (getTransport p) (fromIntegral i) | 
 |  | 
 |  | 
 | -- | Write a type as a byte | 
 | writeType :: (Protocol p, Transport t) => p t -> ThriftType -> IO () | 
 | writeType p t = writeByte p (fromIntegral $ fromEnum t) | 
 |  | 
 | -- | Read a byte as though it were a ThriftType | 
 | readType :: (Protocol p, Transport t) => p t -> IO ThriftType | 
 | readType p = do | 
 |     b <- readByte p | 
 |     return $ toEnum $ fromIntegral b | 
 |  | 
 | floatBits :: Double -> Word64 | 
 | floatBits (D# d#) = W64# (unsafeCoerce# d#) | 
 |  | 
 | floatOfBits :: Word64 -> Double | 
 | floatOfBits (W64# b#) = D# (unsafeCoerce# b#) | 
 |  |