blob: 308ab48dc8bb8a975639e897c58a7ea23365264c [file] [log] [blame]
Bryan Duxburye59a80f2010-09-20 15:21:37 +00001{-# LANGUAGE ExistentialQuantification #-}
2{-# LANGUAGE MagicHash #-}
Bryan Duxbury0781f2b2009-04-07 23:29:42 +00003--
4-- Licensed to the Apache Software Foundation (ASF) under one
5-- or more contributor license agreements. See the NOTICE file
6-- distributed with this work for additional information
7-- regarding copyright ownership. The ASF licenses this file
8-- to you under the Apache License, Version 2.0 (the
9-- "License"); you may not use this file except in compliance
10-- with the License. You may obtain a copy of the License at
11--
12-- http://www.apache.org/licenses/LICENSE-2.0
13--
14-- Unless required by applicable law or agreed to in writing,
15-- software distributed under the License is distributed on an
16-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17-- KIND, either express or implied. See the License for the
18-- specific language governing permissions and limitations
19-- under the License.
20--
21
22module Thrift.Protocol.Binary
23 ( module Thrift.Protocol
24 , BinaryProtocol(..)
25 ) where
26
27import Control.Exception ( throw )
David Reiss752529e2010-01-11 19:12:56 +000028import Control.Monad ( liftM )
Bryan Duxbury0781f2b2009-04-07 23:29:42 +000029
30import Data.Bits
31import Data.Int
32import Data.List ( foldl' )
33
34import GHC.Exts
35import GHC.Word
36
37import Thrift.Protocol
38import Thrift.Transport
39
David Reiss752529e2010-01-11 19:12:56 +000040import qualified Data.ByteString.Lazy.Char8 as LBS
Bryan Duxbury0781f2b2009-04-07 23:29:42 +000041
Bryan Duxburye59a80f2010-09-20 15:21:37 +000042version_mask :: Int
Bryan Duxbury0781f2b2009-04-07 23:29:42 +000043version_mask = 0xffff0000
Bryan Duxburye59a80f2010-09-20 15:21:37 +000044
45version_1 :: Int
Bryan Duxbury0781f2b2009-04-07 23:29:42 +000046version_1 = 0x80010000
47
48data BinaryProtocol a = Transport a => BinaryProtocol a
49
50
51instance Protocol BinaryProtocol where
52 getTransport (BinaryProtocol t) = t
53
54 writeMessageBegin p (n, t, s) = do
55 writeI32 p (version_1 .|. (fromEnum t))
56 writeString p n
57 writeI32 p s
58 writeMessageEnd _ = return ()
59
60 writeStructBegin _ _ = return ()
61 writeStructEnd _ = return ()
62 writeFieldBegin p (_, t, i) = writeType p t >> writeI16 p i
63 writeFieldEnd _ = return ()
64 writeFieldStop p = writeType p T_STOP
65 writeMapBegin p (k, v, n) = writeType p k >> writeType p v >> writeI32 p n
Bryan Duxburye59a80f2010-09-20 15:21:37 +000066 writeMapEnd _ = return ()
Bryan Duxbury0781f2b2009-04-07 23:29:42 +000067 writeListBegin p (t, n) = writeType p t >> writeI32 p n
68 writeListEnd _ = return ()
69 writeSetBegin p (t, n) = writeType p t >> writeI32 p n
70 writeSetEnd _ = return ()
71
David Reiss752529e2010-01-11 19:12:56 +000072 writeBool p b = tWrite (getTransport p) $ LBS.singleton $ toEnum $ if b then 1 else 0
Bryan Duxbury0781f2b2009-04-07 23:29:42 +000073 writeByte p b = tWrite (getTransport p) (getBytes b 1)
74 writeI16 p b = tWrite (getTransport p) (getBytes b 2)
75 writeI32 p b = tWrite (getTransport p) (getBytes b 4)
76 writeI64 p b = tWrite (getTransport p) (getBytes b 8)
77 writeDouble p d = writeI64 p (fromIntegral $ floatBits d)
David Reiss752529e2010-01-11 19:12:56 +000078 writeString p s = writeI32 p (length s) >> tWrite (getTransport p) (LBS.pack s)
Bryan Duxbury0781f2b2009-04-07 23:29:42 +000079 writeBinary = writeString
80
81 readMessageBegin p = do
82 ver <- readI32 p
83 if (ver .&. version_mask /= version_1)
84 then throw $ ProtocolExn PE_BAD_VERSION "Missing version identifier"
85 else do
86 s <- readString p
87 sz <- readI32 p
88 return (s, toEnum $ ver .&. 0xFF, sz)
89 readMessageEnd _ = return ()
90 readStructBegin _ = return ""
91 readStructEnd _ = return ()
92 readFieldBegin p = do
93 t <- readType p
94 n <- if t /= T_STOP then readI16 p else return 0
95 return ("", t, n)
96 readFieldEnd _ = return ()
97 readMapBegin p = do
98 kt <- readType p
99 vt <- readType p
100 n <- readI32 p
101 return (kt, vt, n)
102 readMapEnd _ = return ()
103 readListBegin p = do
104 t <- readType p
105 n <- readI32 p
106 return (t, n)
107 readListEnd _ = return ()
108 readSetBegin p = do
109 t <- readType p
110 n <- readI32 p
111 return (t, n)
112 readSetEnd _ = return ()
113
114 readBool p = (== 1) `fmap` readByte p
115 readByte p = do
116 bs <- tReadAll (getTransport p) 1
117 return $ fromIntegral (composeBytes bs :: Int8)
118 readI16 p = do
119 bs <- tReadAll (getTransport p) 2
120 return $ fromIntegral (composeBytes bs :: Int16)
121 readI32 p = composeBytes `fmap` tReadAll (getTransport p) 4
122 readI64 p = composeBytes `fmap` tReadAll (getTransport p) 8
123 readDouble p = do
124 bs <- readI64 p
125 return $ floatOfBits $ fromIntegral bs
David Reiss752529e2010-01-11 19:12:56 +0000126 readString p = do
127 i <- readI32 p
128 LBS.unpack `liftM` tReadAll (getTransport p) i
129
Bryan Duxbury0781f2b2009-04-07 23:29:42 +0000130 readBinary = readString
131
132
133-- | Write a type as a byte
134writeType :: (Protocol p, Transport t) => p t -> ThriftType -> IO ()
135writeType p t = writeByte p (fromEnum t)
136
137-- | Read a byte as though it were a ThriftType
138readType :: (Protocol p, Transport t) => p t -> IO ThriftType
139readType p = toEnum `fmap` readByte p
140
David Reiss752529e2010-01-11 19:12:56 +0000141composeBytes :: (Bits b) => LBS.ByteString -> b
142composeBytes = (foldl' fn 0) . (map (fromIntegral . fromEnum)) . LBS.unpack
Bryan Duxbury0781f2b2009-04-07 23:29:42 +0000143 where fn acc b = (acc `shiftL` 8) .|. b
144
145getByte :: Bits a => a -> Int -> a
146getByte i n = 255 .&. (i `shiftR` (8 * n))
147
David Reiss752529e2010-01-11 19:12:56 +0000148getBytes :: (Bits a, Integral a) => a -> Int -> LBS.ByteString
Bryan Duxburye59a80f2010-09-20 15:21:37 +0000149getBytes _ 0 = LBS.empty
David Reiss752529e2010-01-11 19:12:56 +0000150getBytes i n = (toEnum $ fromIntegral $ getByte i (n-1)) `LBS.cons` (getBytes i (n-1))
Bryan Duxbury0781f2b2009-04-07 23:29:42 +0000151
152floatBits :: Double -> Word64
153floatBits (D# d#) = W64# (unsafeCoerce# d#)
154
155floatOfBits :: Word64 -> Double
156floatOfBits (W64# b#) = D# (unsafeCoerce# b#)
157