Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 1 | {-# LANGUAGE CPP #-} |
Bryan Duxbury | e59a80f | 2010-09-20 15:21:37 +0000 | [diff] [blame] | 2 | {-# LANGUAGE DeriveDataTypeable #-} |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [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 |
| 24 | ( Protocol(..) |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 25 | , ProtocolExn(..) |
| 26 | , ProtocolExnType(..) |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 27 | , getTypeOf |
| 28 | , runParser |
| 29 | , versionMask |
| 30 | , version1 |
| 31 | , bsToDouble |
Nobuaki Sukegawa | 7c7d679 | 2015-12-09 03:22:35 +0900 | [diff] [blame] | 32 | , bsToDoubleLE |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 33 | ) where |
| 34 | |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 35 | import Control.Exception |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 36 | import Data.Attoparsec.ByteString |
| 37 | import Data.Bits |
| 38 | import Data.ByteString.Lazy (ByteString, toStrict) |
| 39 | import Data.ByteString.Unsafe |
| 40 | import Data.Functor ((<$>)) |
Roger Meier | 6849f20 | 2012-05-18 07:35:19 +0000 | [diff] [blame] | 41 | import Data.Int |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 42 | import Data.Monoid (mempty) |
| 43 | import Data.Text.Lazy (Text) |
| 44 | import Data.Typeable (Typeable) |
| 45 | import Data.Word |
| 46 | import Foreign.Ptr (castPtr) |
| 47 | import Foreign.Storable (Storable, peek, poke) |
| 48 | import System.IO.Unsafe |
| 49 | import qualified Data.ByteString as BS |
| 50 | import qualified Data.HashMap.Strict as Map |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 51 | |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 52 | import Thrift.Types |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 53 | import Thrift.Transport |
| 54 | |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 55 | versionMask :: Int32 |
| 56 | versionMask = fromIntegral (0xffff0000 :: Word32) |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 57 | |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 58 | version1 :: Int32 |
| 59 | version1 = fromIntegral (0x80010000 :: Word32) |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 60 | |
| 61 | class Protocol a where |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 62 | getTransport :: Transport t => a t -> t |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 63 | |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 64 | writeMessageBegin :: Transport t => a t -> (Text, MessageType, Int32) -> IO () |
| 65 | writeMessageEnd :: Transport t => a t -> IO () |
| 66 | writeMessageEnd _ = return () |
| 67 | |
| 68 | readMessageBegin :: Transport t => a t -> IO (Text, MessageType, Int32) |
| 69 | readMessageEnd :: Transport t => a t -> IO () |
| 70 | readMessageEnd _ = return () |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 71 | |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 72 | serializeVal :: Transport t => a t -> ThriftVal -> ByteString |
| 73 | deserializeVal :: Transport t => a t -> ThriftType -> ByteString -> ThriftVal |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 74 | |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 75 | writeVal :: Transport t => a t -> ThriftVal -> IO () |
| 76 | writeVal p = tWrite (getTransport p) . serializeVal p |
| 77 | readVal :: Transport t => a t -> ThriftType -> IO ThriftVal |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 78 | |
| 79 | data ProtocolExnType |
| 80 | = PE_UNKNOWN |
| 81 | | PE_INVALID_DATA |
| 82 | | PE_NEGATIVE_SIZE |
| 83 | | PE_SIZE_LIMIT |
| 84 | | PE_BAD_VERSION |
Jens Geyer | 6d1a83a | 2014-05-03 00:49:05 +0200 | [diff] [blame] | 85 | | PE_NOT_IMPLEMENTED |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 86 | | PE_MISSING_REQUIRED_FIELD |
Bryan Duxbury | 0781f2b | 2009-04-07 23:29:42 +0000 | [diff] [blame] | 87 | deriving ( Eq, Show, Typeable ) |
| 88 | |
| 89 | data ProtocolExn = ProtocolExn ProtocolExnType String |
| 90 | deriving ( Show, Typeable ) |
| 91 | instance Exception ProtocolExn |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 92 | |
| 93 | getTypeOf :: ThriftVal -> ThriftType |
| 94 | getTypeOf v = case v of |
| 95 | TStruct{} -> T_STRUCT Map.empty |
| 96 | TMap{} -> T_MAP T_VOID T_VOID |
| 97 | TList{} -> T_LIST T_VOID |
| 98 | TSet{} -> T_SET T_VOID |
| 99 | TBool{} -> T_BOOL |
| 100 | TByte{} -> T_BYTE |
| 101 | TI16{} -> T_I16 |
| 102 | TI32{} -> T_I32 |
| 103 | TI64{} -> T_I64 |
| 104 | TString{} -> T_STRING |
Nobuaki Sukegawa | e68ccc2 | 2015-12-13 21:45:39 +0900 | [diff] [blame] | 105 | TBinary{} -> T_BINARY |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 106 | TDouble{} -> T_DOUBLE |
| 107 | |
| 108 | runParser :: (Protocol p, Transport t, Show a) => p t -> Parser a -> IO a |
| 109 | runParser prot p = refill >>= getResult . parse p |
| 110 | where |
Nobuaki Sukegawa | ef2b528 | 2015-12-11 02:24:17 +0900 | [diff] [blame] | 111 | refill = handle handleEOF $ toStrict <$> tReadAll (getTransport prot) 1 |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 112 | getResult (Done _ a) = return a |
| 113 | getResult (Partial k) = refill >>= getResult . k |
| 114 | getResult f = throw $ ProtocolExn PE_INVALID_DATA (show f) |
| 115 | |
| 116 | handleEOF :: SomeException -> IO BS.ByteString |
| 117 | handleEOF = const $ return mempty |
| 118 | |
| 119 | -- | Converts a ByteString to a Floating point number |
| 120 | -- The ByteString is assumed to be encoded in network order (Big Endian) |
| 121 | -- therefore the behavior of this function varies based on whether the local |
| 122 | -- machine is big endian or little endian. |
| 123 | bsToDouble :: BS.ByteString -> Double |
Nobuaki Sukegawa | 7c7d679 | 2015-12-09 03:22:35 +0900 | [diff] [blame] | 124 | bsToDoubleLE :: BS.ByteString -> Double |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 125 | #if __BYTE_ORDER == __LITTLE_ENDIAN |
Nobuaki Sukegawa | 7c7d679 | 2015-12-09 03:22:35 +0900 | [diff] [blame] | 126 | bsToDouble bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBsSwapped |
| 127 | bsToDoubleLE bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBs |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 128 | #else |
Nobuaki Sukegawa | 7c7d679 | 2015-12-09 03:22:35 +0900 | [diff] [blame] | 129 | bsToDouble bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBs |
| 130 | bsToDoubleLE bs = unsafeDupablePerformIO $ unsafeUseAsCString bs castBsSwapped |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 131 | #endif |
| 132 | |
Nobuaki Sukegawa | 7c7d679 | 2015-12-09 03:22:35 +0900 | [diff] [blame] | 133 | |
| 134 | castBsSwapped chrPtr = do |
| 135 | w <- peek (castPtr chrPtr) |
| 136 | poke (castPtr chrPtr) (byteSwap w) |
| 137 | peek (castPtr chrPtr) |
| 138 | castBs = peek . castPtr |
| 139 | |
Noam Zilberstein | af5d64a | 2014-07-31 15:44:13 -0700 | [diff] [blame] | 140 | -- | Swap endianness of a 64-bit word |
| 141 | byteSwap :: Word64 -> Word64 |
| 142 | byteSwap w = (w `shiftL` 56 .&. 0xFF00000000000000) .|. |
| 143 | (w `shiftL` 40 .&. 0x00FF000000000000) .|. |
| 144 | (w `shiftL` 24 .&. 0x0000FF0000000000) .|. |
| 145 | (w `shiftL` 8 .&. 0x000000FF00000000) .|. |
| 146 | (w `shiftR` 8 .&. 0x00000000FF000000) .|. |
| 147 | (w `shiftR` 24 .&. 0x0000000000FF0000) .|. |
| 148 | (w `shiftR` 40 .&. 0x000000000000FF00) .|. |
| 149 | (w `shiftR` 56 .&. 0x00000000000000FF) |