{-# LANGUAGE FlexibleContexts #-} -- | -- Module : Database.Monarch.Utils -- Copyright : 2013 Noriyuki OHKAWA -- License : BSD3 -- -- Maintainer : n.ohkawa@gmail.com -- Stability : experimental -- Portability : unknown -- -- Internal utilities. -- module Database.Monarch.Utils ( toCode , putMagic, putOptions , lengthBS32, lengthLBS32 , fromLBS , yieldRequest , responseCode , parseLBS, parseBS , parseWord32, parseInt64, parseDouble , parseKeyValue , communicate ) where import Data.Int import Data.Bits import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import qualified Data.Binary as B import Data.Binary.Put (runPut, putWord32be) import Data.Binary.Get (runGet, getWord32be) import Control.Applicative import Control.Monad.IO.Class import Control.Monad.Trans.Control import Database.Monarch.Types class BitFlag32 a where fromOption :: a -> Int32 instance BitFlag32 ExtOption where fromOption RecordLocking = 0x1 fromOption GlobalLocking = 0x2 instance BitFlag32 RestoreOption where fromOption ConsistencyChecking = 0x1 instance BitFlag32 MiscOption where fromOption NoUpdateLog = 0x1 -- | Convert status code toCode :: Int -> Code toCode 0 = Success toCode 1 = InvalidOperation toCode 2 = HostNotFound toCode 3 = ConnectionRefused toCode 4 = SendError toCode 5 = ReceiveError toCode 6 = ExistingRecord toCode 7 = NoRecordFound toCode 9999 = MiscellaneousError toCode _ = error "Invalid Code" -- | TokyoTyrant Original Binary Protocal magic id. -- -- Example: -- -- >>> :m +Data.ByteString.Char8 -- >>> :set -XOverloadedStrings -- >>> fromLBS (runPut $ putMagic 0x10) == "\xC8\x10" -- True -- putMagic :: B.Word8 -> B.Put putMagic magic = B.putWord8 0xC8 >> B.putWord8 magic -- | Option -- -- Example: -- -- >>> :m +Data.ByteString.Char8 -- >>> :set -XOverloadedStrings -- >>> fromLBS (runPut $ putOptions [RecordLocking]) == "\0\0\0\1" -- True -- >>> fromLBS (runPut $ putOptions [GlobalLocking]) == "\0\0\0\2" -- True -- >>> fromLBS (runPut $ putOptions [RecordLocking, GlobalLocking]) == "\0\0\0\3" -- True -- putOptions :: BitFlag32 option => [option] -> B.Put putOptions = putWord32be . fromIntegral . foldl (.|.) 0 . map fromOption -- | Get Length -- -- Example: -- -- >>> :m +Data.ByteString.Char8 -- >>> :set -XOverloadedStrings -- >>> lengthBS32 "test" -- 4 -- >>> lengthBS32 "" -- 0 -- lengthBS32 :: BS.ByteString -> B.Word32 lengthBS32 = fromIntegral . BS.length -- | Get Length -- -- Example: -- -- >>> :m +Data.ByteString.Lazy.Char8 -- >>> :set -XOverloadedStrings -- >>> lengthLBS32 "test" -- 4 -- >>> lengthLBS32 "" -- 0 -- lengthLBS32 :: LBS.ByteString -> B.Word32 lengthLBS32 = fromIntegral . LBS.length -- | Convert -- -- Example: -- -- >>> :m +Data.ByteString.Lazy.Char8 -- >>> :set -XOverloadedStrings -- >>> fromLBS "test" -- "test" -- fromLBS :: LBS.ByteString -> BS.ByteString fromLBS = BS.pack . LBS.unpack -- | Send request. yieldRequest :: ( MonadBaseControl IO m , MonadIO m ) => B.Put -> MonarchT m () yieldRequest = sendLBS . runPut -- | Receive response code. responseCode :: ( MonadBaseControl IO m , MonadIO m ) => MonarchT m Code responseCode = toCode . fromIntegral . runGet B.getWord8 <$> recvLBS 1 -- | Parse byte string (lazy) value. parseLBS :: ( MonadBaseControl IO m , MonadIO m ) => MonarchT m LBS.ByteString parseLBS = recvLBS 4 >>= recvLBS . fromIntegral . runGet getWord32be -- | Parse byte string (strict) value. parseBS :: ( MonadBaseControl IO m , MonadIO m ) => MonarchT m BS.ByteString parseBS = fromLBS <$> parseLBS -- | Parse Word32 value. parseWord32 :: ( MonadBaseControl IO m , MonadIO m ) => MonarchT m B.Word32 parseWord32 = runGet getWord32be <$> recvLBS 4 -- | Parse Int64 value. parseInt64 :: ( MonadBaseControl IO m , MonadIO m ) => MonarchT m Int64 parseInt64 = runGet (B.get :: B.Get Int64) <$> recvLBS 8 -- | Parse Double value. parseDouble :: ( MonadBaseControl IO m , MonadIO m ) => MonarchT m Double parseDouble = do integ <- fromIntegral <$> parseInt64 fract <- fromIntegral <$> parseInt64 return $ integ + fract * 1e-12 -- | Parse key value pair. parseKeyValue :: ( MonadBaseControl IO m , MonadIO m ) => MonarchT m (BS.ByteString, BS.ByteString) parseKeyValue = do ksiz <- recvLBS 4 vsiz <- recvLBS 4 key <- recvLBS . fromIntegral $ runGet getWord32be ksiz value <- recvLBS . fromIntegral $ runGet getWord32be vsiz return (fromLBS key, fromLBS value) -- | Make a query. communicate :: ( MonadBaseControl IO m , MonadIO m) => B.Put -> (Code -> MonarchT m a) -> MonarchT m a communicate makeRequest makeResponse = yieldRequest makeRequest >> responseCode >>= makeResponse