| Bryan Duxbury | e59a80f | 2010-09-20 15:21:37 +0000 | [diff] [blame] | 1 | {-# LANGUAGE ExistentialQuantification #-} | 
 | 2 | {-# LANGUAGE MagicHash #-} | 
| Roger Meier | 6849f20 | 2012-05-18 07:35:19 +0000 | [diff] [blame] | 3 | {-# LANGUAGE OverloadedStrings #-} | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 4 | -- | 
 | 5 | -- Licensed to the Apache Software Foundation (ASF) under one | 
 | 6 | -- or more contributor license agreements. See the NOTICE file | 
 | 7 | -- distributed with this work for additional information | 
 | 8 | -- regarding copyright ownership. The ASF licenses this file | 
 | 9 | -- to you under the Apache License, Version 2.0 (the | 
 | 10 | -- "License"); you may not use this file except in compliance | 
 | 11 | -- with the License. You may obtain a copy of the License at | 
 | 12 | -- | 
 | 13 | --   http://www.apache.org/licenses/LICENSE-2.0 | 
 | 14 | -- | 
 | 15 | -- Unless required by applicable law or agreed to in writing, | 
 | 16 | -- software distributed under the License is distributed on an | 
 | 17 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 | 18 | -- KIND, either express or implied. See the License for the | 
 | 19 | -- specific language governing permissions and limitations | 
 | 20 | -- under the License. | 
 | 21 | -- | 
 | 22 |  | 
 | 23 | module Thrift.Protocol.Binary | 
 | 24 |     ( module Thrift.Protocol | 
 | 25 |     , BinaryProtocol(..) | 
 | 26 |     ) where | 
 | 27 |  | 
 | 28 | import Control.Exception ( throw ) | 
| David Reiss | 752529e | 2010-01-11 19:12:56 +0000 | [diff] [blame] | 29 | import Control.Monad ( liftM ) | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 30 |  | 
| Christian Lavoie | ae7f7fa | 2010-11-02 21:42:53 +0000 | [diff] [blame] | 31 | import qualified Data.Binary | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 32 | import Data.Bits | 
 | 33 | import Data.Int | 
| Roger Meier | 6849f20 | 2012-05-18 07:35:19 +0000 | [diff] [blame] | 34 | import Data.Text.Lazy.Encoding ( decodeUtf8, encodeUtf8 ) | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 35 |  | 
 | 36 | import GHC.Exts | 
 | 37 | import GHC.Word | 
 | 38 |  | 
 | 39 | import Thrift.Protocol | 
 | 40 | import Thrift.Transport | 
 | 41 |  | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 42 | import qualified Data.ByteString.Lazy as LBS | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 43 |  | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 44 | version_mask :: Int32 | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 45 | version_mask = 0xffff0000 | 
| Bryan Duxbury | e59a80f | 2010-09-20 15:21:37 +0000 | [diff] [blame] | 46 |  | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 47 | version_1 :: Int32 | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 48 | version_1    = 0x80010000 | 
 | 49 |  | 
 | 50 | data BinaryProtocol a = Transport a => BinaryProtocol a | 
 | 51 |  | 
 | 52 |  | 
 | 53 | instance Protocol BinaryProtocol where | 
 | 54 |     getTransport (BinaryProtocol t) = t | 
 | 55 |  | 
 | 56 |     writeMessageBegin p (n, t, s) = do | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 57 |         writeI32 p (version_1 .|. (fromIntegral $ fromEnum t)) | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 58 |         writeString p n | 
 | 59 |         writeI32 p s | 
 | 60 |     writeMessageEnd _ = return () | 
 | 61 |  | 
 | 62 |     writeStructBegin _ _ = return () | 
 | 63 |     writeStructEnd _ = return () | 
 | 64 |     writeFieldBegin p (_, t, i) = writeType p t >> writeI16 p i | 
 | 65 |     writeFieldEnd _ = return () | 
 | 66 |     writeFieldStop p = writeType p T_STOP | 
 | 67 |     writeMapBegin p (k, v, n) = writeType p k >> writeType p v >> writeI32 p n | 
| Bryan Duxbury | e59a80f | 2010-09-20 15:21:37 +0000 | [diff] [blame] | 68 |     writeMapEnd _ = return () | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 69 |     writeListBegin p (t, n) = writeType p t >> writeI32 p n | 
 | 70 |     writeListEnd _ = return () | 
 | 71 |     writeSetBegin p (t, n) = writeType p t >> writeI32 p n | 
 | 72 |     writeSetEnd _ = return () | 
 | 73 |  | 
| Christian Lavoie | ae7f7fa | 2010-11-02 21:42:53 +0000 | [diff] [blame] | 74 |     writeBool p b = tWrite (getTransport p) $ LBS.singleton $ toEnum $ if b then 1 else 0 | 
 | 75 |     writeByte p b = tWrite (getTransport p) $ Data.Binary.encode b | 
 | 76 |     writeI16 p b = tWrite (getTransport p) $ Data.Binary.encode b | 
 | 77 |     writeI32 p b = tWrite (getTransport p) $ Data.Binary.encode b | 
 | 78 |     writeI64 p b = tWrite (getTransport p) $ Data.Binary.encode b | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 79 |     writeDouble p d = writeI64 p (fromIntegral $ floatBits d) | 
| Roger Meier | 6849f20 | 2012-05-18 07:35:19 +0000 | [diff] [blame] | 80 |     writeString p s = writeI32 p (fromIntegral $ LBS.length s') >> tWrite (getTransport p) s' | 
 | 81 |       where | 
 | 82 |         s' = encodeUtf8 s | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 83 |     writeBinary p s = writeI32 p (fromIntegral $ LBS.length s) >> tWrite (getTransport p) s | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 84 |  | 
 | 85 |     readMessageBegin p = do | 
 | 86 |         ver <- readI32 p | 
 | 87 |         if (ver .&. version_mask /= version_1) | 
 | 88 |             then throw $ ProtocolExn PE_BAD_VERSION "Missing version identifier" | 
 | 89 |             else do | 
 | 90 |               s <- readString p | 
 | 91 |               sz <- readI32 p | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 92 |               return (s, toEnum $ fromIntegral $ ver .&. 0xFF, sz) | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 93 |     readMessageEnd _ = return () | 
 | 94 |     readStructBegin _ = return "" | 
 | 95 |     readStructEnd _ = return () | 
 | 96 |     readFieldBegin p = do | 
 | 97 |         t <- readType p | 
 | 98 |         n <- if t /= T_STOP then readI16 p else return 0 | 
 | 99 |         return ("", t, n) | 
 | 100 |     readFieldEnd _ = return () | 
 | 101 |     readMapBegin p = do | 
 | 102 |         kt <- readType p | 
 | 103 |         vt <- readType p | 
 | 104 |         n <- readI32 p | 
 | 105 |         return (kt, vt, n) | 
 | 106 |     readMapEnd _ = return () | 
 | 107 |     readListBegin p = do | 
 | 108 |         t <- readType p | 
 | 109 |         n <- readI32 p | 
 | 110 |         return (t, n) | 
 | 111 |     readListEnd _ = return () | 
 | 112 |     readSetBegin p = do | 
 | 113 |         t <- readType p | 
 | 114 |         n <- readI32 p | 
 | 115 |         return (t, n) | 
 | 116 |     readSetEnd _ = return () | 
 | 117 |  | 
 | 118 |     readBool p = (== 1) `fmap` readByte p | 
| Christian Lavoie | ae7f7fa | 2010-11-02 21:42:53 +0000 | [diff] [blame] | 119 |  | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 120 |     readByte p = do | 
 | 121 |         bs <- tReadAll (getTransport p) 1 | 
| Christian Lavoie | ae7f7fa | 2010-11-02 21:42:53 +0000 | [diff] [blame] | 122 |         return $ Data.Binary.decode bs | 
 | 123 |  | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 124 |     readI16 p = do | 
 | 125 |         bs <- tReadAll (getTransport p) 2 | 
| Christian Lavoie | ae7f7fa | 2010-11-02 21:42:53 +0000 | [diff] [blame] | 126 |         return $ Data.Binary.decode bs | 
 | 127 |  | 
 | 128 |     readI32 p = do | 
 | 129 |         bs <- tReadAll (getTransport p) 4 | 
 | 130 |         return $ Data.Binary.decode bs | 
 | 131 |  | 
 | 132 |     readI64 p = do | 
 | 133 |         bs <- tReadAll (getTransport p) 8 | 
 | 134 |         return $ Data.Binary.decode bs | 
 | 135 |  | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 136 |     readDouble p = do | 
 | 137 |         bs <- readI64 p | 
 | 138 |         return $ floatOfBits $ fromIntegral bs | 
| Christian Lavoie | ae7f7fa | 2010-11-02 21:42:53 +0000 | [diff] [blame] | 139 |  | 
| David Reiss | 752529e | 2010-01-11 19:12:56 +0000 | [diff] [blame] | 140 |     readString p = do | 
 | 141 |         i <- readI32 p | 
| Roger Meier | 6849f20 | 2012-05-18 07:35:19 +0000 | [diff] [blame] | 142 |         decodeUtf8 `liftM` tReadAll (getTransport p) (fromIntegral i) | 
| Christian Lavoie | ae7f7fa | 2010-11-02 21:42:53 +0000 | [diff] [blame] | 143 |  | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 144 |     readBinary p = do | 
 | 145 |         i <- readI32 p | 
 | 146 |         tReadAll (getTransport p) (fromIntegral i) | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 147 |  | 
 | 148 |  | 
 | 149 | -- | Write a type as a byte | 
 | 150 | writeType :: (Protocol p, Transport t) => p t -> ThriftType -> IO () | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 151 | writeType p t = writeByte p (fromIntegral $ fromEnum t) | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 152 |  | 
 | 153 | -- | Read a byte as though it were a ThriftType | 
 | 154 | readType :: (Protocol p, Transport t) => p t -> IO ThriftType | 
| Bryan Duxbury | 75a33e8 | 2010-09-22 00:48:56 +0000 | [diff] [blame] | 155 | readType p = do | 
 | 156 |     b <- readByte p | 
 | 157 |     return $ toEnum $ fromIntegral b | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 158 |  | 
| Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 159 | floatBits :: Double -> Word64 | 
 | 160 | floatBits (D# d#) = W64# (unsafeCoerce# d#) | 
 | 161 |  | 
 | 162 | floatOfBits :: Word64 -> Double | 
 | 163 | floatOfBits (W64# b#) = D# (unsafeCoerce# b#) | 
 | 164 |  |