packages feed

cloudi (empty) → 1.7.0

raw patch · 11 files changed

+2533/−0 lines, 11 filesdep +arraydep +basedep +binarysetup-changed

Dependencies added: array, base, binary, bytestring, containers, network, time, unix, zlib

Files

+ LICENSE view
@@ -0,0 +1,36 @@+BSD LICENSE
+
+Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+    * All advertising materials mentioning features or use of this
+      software must display the following acknowledgment:
+        This product includes software developed by Michael Truog
+    * The name of the author may not be used to endorse or promote
+      products derived from this software without specific prior
+      written permission
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+ README.markdown view
@@ -0,0 +1,14 @@+`cloudi_api_haskell`+====================++Haskell [CloudI API](http://cloudi.org/api.html#1_Intro)++Build+-----++    cabal sandbox init+    cabal update+    cabal install --only-dependencies+    cabal configure+    cabal build+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cloudi.cabal view
@@ -0,0 +1,41 @@+name:                cloudi+version:             1.7.0+synopsis:            Haskell CloudI API+description:         Haskell CloudI API+homepage:            https://github.com/CloudI/cloudi_api_haskell+license:             BSD4+license-file:        LICENSE+author:              Michael Truog+maintainer:          mjtruog@gmail.com+copyright:           2017 Michael Truog+category:            Foreign+stability:           provisional+build-type:          Simple+cabal-version:       >= 1.22+tested-with:         GHC == 7.10.3, GHC == 8.0.2+extra-source-files:  README.markdown++library+  hs-source-dirs:    src+  exposed-modules:     Foreign.CloudI+                     , Foreign.Erlang+                     , Foreign.Erlang.Pid+                     , Foreign.Erlang.Port+                     , Foreign.Erlang.Reference+                     , Foreign.Erlang.Function+  other-modules:       Foreign.CloudI.Instance+  ghc-options:       -Wall -funbox-strict-fields+  build-depends:       base >= 4.8.2.0 && < 5+                     , binary >= 0.8.4.0+                     , bytestring >= 0.10.4+                     , array+                     , containers+                     , network+                     , time+                     , unix+                     , zlib+  default-language:  Haskell2010++source-repository head+  type:     git+  location: https://github.com/CloudI/cloudi_api_haskell.git
+ src/Foreign/CloudI.hs view
@@ -0,0 +1,1179 @@+{--*-Mode:haskell;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-+  ex: set ft=haskell fenc=utf-8 sts=4 ts=4 sw=4 et nomod: -}++{-++  BSD LICENSE++  Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>+  All rights reserved.++  Redistribution and use in source and binary forms, with or without+  modification, are permitted provided that the following conditions are met:++      * Redistributions of source code must retain the above copyright+        notice, this list of conditions and the following disclaimer.+      * Redistributions in binary form must reproduce the above copyright+        notice, this list of conditions and the following disclaimer in+        the documentation and/or other materials provided with the+        distribution.+      * All advertising materials mentioning features or use of this+        software must display the following acknowledgment:+          This product includes software developed by Michael Truog+      * The name of the author may not be used to endorse or promote+        products derived from this software without specific prior+        written permission++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+  DAMAGE.++ -}++module Foreign.CloudI+    ( Instance.RequestType(..)+    , Instance.Source+    , Instance.Response(..)+    , Instance.Callback+    , Instance.T+    , transIdNull+    , invalidInputError+    , messageDecodingError+    , terminateError+    , Exception+    , api+    , threadCount+    , subscribe+    , subscribeCount+    , unsubscribe+    , sendAsync+    , sendSync+    , mcastAsync+    , forward_+    , forwardAsync+    , forwardSync+    , return_+    , returnAsync+    , returnSync+    , recvAsync+    , processIndex+    , processCount+    , processCountMax+    , processCountMin+    , prefix+    , timeoutInitialize+    , timeoutAsync+    , timeoutSync+    , timeoutTerminate+    , poll+    , threadCreate+    , threadsWait+    , infoKeyValueParse+    ) where++import Prelude hiding (init,length)+import Data.Bits (shiftL,(.|.))+import Data.Maybe (fromMaybe)+import Data.Typeable (Typeable)+import qualified Control.Exception as Exception+import qualified Control.Concurrent as Concurrent+import qualified Data.Array.IArray as IArray+import qualified Data.Binary.Get as Get+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Builder as Builder+import qualified Data.ByteString.Char8 as Char8+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.List as List+import qualified Data.Map.Strict as Map+import qualified Data.Monoid as Monoid+import qualified Data.Sequence as Sequence+import qualified Data.Time.Clock as Clock+import qualified Data.Time.Clock.POSIX as POSIX (getPOSIXTime)+import qualified Data.Word as Word+import qualified Foreign.C.Types as C+import qualified Foreign.Erlang as Erlang+import qualified Foreign.CloudI.Instance as Instance+import qualified System.IO as SysIO+import qualified System.IO.Unsafe as Unsafe+import qualified System.Posix.Env as POSIX (getEnv)+type Array = IArray.Array+type Builder = Builder.Builder+type ByteString = ByteString.ByteString+type Get = Get.Get+type Handle = SysIO.Handle+type LazyByteString = LazyByteString.ByteString+type Map = Map.Map+type RequestType = Instance.RequestType+type SomeException = Exception.SomeException+type Source = Instance.Source+type ThreadId = Concurrent.ThreadId+type Word32 = Word.Word32++messageInit :: Word32+messageInit = 1+messageSendAsync :: Word32+messageSendAsync = 2+messageSendSync :: Word32+messageSendSync = 3+messageRecvAsync :: Word32+messageRecvAsync = 4+messageReturnAsync :: Word32+messageReturnAsync = 5+messageReturnSync :: Word32+messageReturnSync = 6+messageReturnsAsync :: Word32+messageReturnsAsync = 7+messageKeepalive :: Word32+messageKeepalive = 8+messageReinit :: Word32+messageReinit = 9+messageSubscribeCount :: Word32+messageSubscribeCount = 10+messageTerm :: Word32+messageTerm = 11++data Message =+      MessageSend (+        RequestType, ByteString, ByteString, ByteString, ByteString,+        Int, Int, ByteString, Source)+    | MessageKeepalive++transIdNull :: ByteString+transIdNull = Char8.pack $ List.replicate 16 '\0'++invalidInputError :: String+invalidInputError = "Invalid Input"+messageDecodingError :: String+messageDecodingError = "Message Decoding Error"+terminateError :: String+terminateError = "Terminate"++data Exception s =+      ReturnSync (Instance.T s)+    | ReturnAsync (Instance.T s)+    | ForwardSync (Instance.T s)+    | ForwardAsync (Instance.T s)+    deriving (Show, Typeable)++instance Typeable s => Exception.Exception (Exception s)++printException :: String -> IO ()+printException str =+    SysIO.hPutStrLn SysIO.stderr ("Exception: " ++ str)++printError :: String -> IO ()+printError str =+    SysIO.hPutStrLn SysIO.stderr ("Error: " ++ str)++data CallbackResult s =+      ReturnI (ByteString, ByteString, s, Instance.T s)+    | ForwardI (ByteString, ByteString, ByteString, Int, Int,+                s, Instance.T s)+    | Finished (Instance.T s)++type Result a = Either String a++api :: Typeable s => Int -> s ->+    IO (Result (Instance.T s))+api threadIndex state = do+    SysIO.hSetEncoding SysIO.stdout SysIO.utf8+    SysIO.hSetBuffering SysIO.stdout SysIO.LineBuffering+    SysIO.hSetEncoding SysIO.stderr SysIO.utf8+    SysIO.hSetBuffering SysIO.stderr SysIO.LineBuffering+    protocolValue <- POSIX.getEnv "CLOUDI_API_INIT_PROTOCOL"+    bufferSizeValue <- POSIX.getEnv "CLOUDI_API_INIT_BUFFER_SIZE"+    case (protocolValue, bufferSizeValue) of+        (Just protocol, Just bufferSizeStr) ->+            let bufferSize = read bufferSizeStr :: Int+                fd = C.CInt $ fromIntegral (threadIndex + 3)+                useHeader = protocol /= "udp"+                timeoutTerminate' = 1000+                initTerms = Erlang.OtpErlangAtom (Char8.pack "init")+            in+            case Erlang.termToBinary initTerms (-1) of+                Left err ->+                    return $ Left $ show err+                Right initBinary -> do+                    api0 <- Instance.make state protocol fd+                        useHeader bufferSize timeoutTerminate'+                    send api0 initBinary+                    result <- pollRequest api0 (-1) False+                    case result of+                        Left err ->+                            return $ Left err+                        Right (_, api1) ->+                            return $ Right api1+        (_, _) ->+            return $ Left invalidInputError++threadCount :: IO (Result Int)+threadCount = do+    threadCountValue <- POSIX.getEnv "CLOUDI_API_INIT_THREAD_COUNT"+    case threadCountValue of+        Nothing ->+            return $ Left invalidInputError+        Just threadCountStr ->+            return $ Right (read threadCountStr :: Int)++subscribe :: Instance.T s -> ByteString -> Instance.Callback s ->+    IO (Result (Instance.T s))+subscribe api0 pattern f =+    let subscribeTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "subscribe")+            , Erlang.OtpErlangString pattern]+    in+    case Erlang.termToBinary subscribeTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right subscribeBinary -> do+            send api0 subscribeBinary+            return $ Right $ Instance.callbacksAdd api0 pattern f++subscribeCount :: Typeable s => Instance.T s -> ByteString ->+    IO (Result (Int, Instance.T s))+subscribeCount api0 pattern =+    let subscribeCountTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "subscribe_count")+            , Erlang.OtpErlangString pattern]+    in+    case Erlang.termToBinary subscribeCountTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right subscribeCountBinary -> do+            send api0 subscribeCountBinary+            result <- pollRequest api0 (-1) False+            case result of+                Left err ->+                    return $ Left err+                Right (_, api1@Instance.T{Instance.subscribeCount = count}) ->+                    return $ Right (count, api1)++unsubscribe :: Instance.T s -> ByteString ->+    IO (Result (Instance.T s))+unsubscribe api0 pattern =+    let unsubscribeTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "unsubscribe")+            , Erlang.OtpErlangString pattern]+    in+    case Erlang.termToBinary unsubscribeTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right unsubscribeBinary -> do+            send api0 unsubscribeBinary+            return $ Right $ Instance.callbacksRemove api0 pattern++sendAsync :: Typeable s => Instance.T s -> ByteString -> ByteString ->+    Maybe Int -> Maybe ByteString -> Maybe Int ->+    IO (Result (ByteString, Instance.T s))+sendAsync api0@Instance.T{+      Instance.timeoutAsync = timeoutAsync'+    , Instance.priorityDefault = priorityDefault}+    name request timeoutOpt requestInfoOpt priorityOpt =+    let timeout = fromMaybe timeoutAsync' timeoutOpt+        requestInfo = fromMaybe ByteString.empty requestInfoOpt+        priority = fromMaybe priorityDefault priorityOpt+        sendAsyncTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "send_async")+            , Erlang.OtpErlangString name+            , Erlang.OtpErlangBinary requestInfo+            , Erlang.OtpErlangBinary request+            , Erlang.OtpErlangInteger timeout+            , Erlang.OtpErlangInteger priority]+    in+    case Erlang.termToBinary sendAsyncTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right sendAsyncBinary -> do+            send api0 sendAsyncBinary+            result <- pollRequest api0 (-1) False+            case result of+                Left err ->+                    return $ Left err+                Right (_, api1@Instance.T{Instance.transId = transId}) ->+                    return $ Right (transId, api1)++sendSync :: Typeable s => Instance.T s -> ByteString -> ByteString ->+    Maybe Int -> Maybe ByteString -> Maybe Int ->+    IO (Result (ByteString, ByteString, ByteString, Instance.T s))+sendSync api0@Instance.T{+      Instance.timeoutSync = timeoutSync'+    , Instance.priorityDefault = priorityDefault}+    name request timeoutOpt requestInfoOpt priorityOpt =+    let timeout = fromMaybe timeoutSync' timeoutOpt+        requestInfo = fromMaybe ByteString.empty requestInfoOpt+        priority = fromMaybe priorityDefault priorityOpt+        sendSyncTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "send_sync")+            , Erlang.OtpErlangString name+            , Erlang.OtpErlangBinary requestInfo+            , Erlang.OtpErlangBinary request+            , Erlang.OtpErlangInteger timeout+            , Erlang.OtpErlangInteger priority]+    in+    case Erlang.termToBinary sendSyncTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right sendSyncBinary -> do+            send api0 sendSyncBinary+            result <- pollRequest api0 (-1) False+            case result of+                Left err ->+                    return $ Left err+                Right (_, api1@Instance.T{+                      Instance.responseInfo = responseInfo+                    , Instance.response = response+                    , Instance.transId = transId}) ->+                    return $ Right (responseInfo, response, transId, api1)++mcastAsync :: Typeable s => Instance.T s -> ByteString -> ByteString ->+    Maybe Int -> Maybe ByteString -> Maybe Int ->+    IO (Result (Array Int ByteString, Instance.T s))+mcastAsync api0@Instance.T{+      Instance.timeoutAsync = timeoutAsync'+    , Instance.priorityDefault = priorityDefault}+    name request timeoutOpt requestInfoOpt priorityOpt =+    let timeout = fromMaybe timeoutAsync' timeoutOpt+        requestInfo = fromMaybe ByteString.empty requestInfoOpt+        priority = fromMaybe priorityDefault priorityOpt+        mcastAsyncTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "mcast_async")+            , Erlang.OtpErlangString name+            , Erlang.OtpErlangBinary requestInfo+            , Erlang.OtpErlangBinary request+            , Erlang.OtpErlangInteger timeout+            , Erlang.OtpErlangInteger priority]+    in+    case Erlang.termToBinary mcastAsyncTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right mcastAsyncBinary -> do+            send api0 mcastAsyncBinary+            result <- pollRequest api0 (-1) False+            case result of+                Left err ->+                    return $ Left err+                Right (_, api1@Instance.T{Instance.transIds = transIds}) ->+                    return $ Right (transIds, api1)++forward_ :: Typeable s =>+    Instance.T s -> Instance.RequestType -> ByteString ->+    ByteString -> ByteString -> Int -> Int -> ByteString -> Source ->+    IO ()+forward_ api0 Instance.ASYNC = forwardAsync api0+forward_ api0 Instance.SYNC = forwardSync api0++forwardAsyncI :: Instance.T s -> ByteString ->+    ByteString -> ByteString -> Int -> Int -> ByteString -> Source ->+    IO (Result (Instance.T s))+forwardAsyncI api0@Instance.T{+      Instance.requestTimeoutAdjustment = requestTimeoutAdjustment+    , Instance.requestTimer = requestTimer+    , Instance.requestTimeout = requestTimeout}+    name responseInfo response timeout priority transId pid = do+    timeoutNew <- if requestTimeoutAdjustment && timeout == requestTimeout then+            timeoutAdjustment requestTimer timeout+        else+            return timeout+    let forwardTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "forward_async")+            , Erlang.OtpErlangString name+            , Erlang.OtpErlangBinary responseInfo+            , Erlang.OtpErlangBinary response+            , Erlang.OtpErlangInteger timeoutNew+            , Erlang.OtpErlangInteger priority+            , Erlang.OtpErlangBinary transId+            , Erlang.OtpErlangPid pid]+    case Erlang.termToBinary forwardTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right forwardBinary -> do+            send api0 forwardBinary+            return $ Right api0++forwardAsync :: Typeable s => Instance.T s -> ByteString ->+    ByteString -> ByteString -> Int -> Int -> ByteString -> Source ->+    IO ()+forwardAsync api0 name responseInfo response timeout priority transId pid = do+    result <- forwardAsyncI api0+        name responseInfo response timeout priority transId pid+    case result of+        Left err ->+            error err+        Right api1 ->+            Exception.throwIO $ ForwardAsync api1++forwardSyncI :: Instance.T s -> ByteString ->+    ByteString -> ByteString -> Int -> Int -> ByteString -> Source ->+    IO (Result (Instance.T s))+forwardSyncI api0@Instance.T{+      Instance.requestTimeoutAdjustment = requestTimeoutAdjustment+    , Instance.requestTimer = requestTimer+    , Instance.requestTimeout = requestTimeout}+    name responseInfo response timeout priority transId pid = do+    timeoutNew <- if requestTimeoutAdjustment && timeout == requestTimeout then+            timeoutAdjustment requestTimer timeout+        else+            return timeout+    let forwardTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "forward_sync")+            , Erlang.OtpErlangString name+            , Erlang.OtpErlangBinary responseInfo+            , Erlang.OtpErlangBinary response+            , Erlang.OtpErlangInteger timeoutNew+            , Erlang.OtpErlangInteger priority+            , Erlang.OtpErlangBinary transId+            , Erlang.OtpErlangPid pid]+    case Erlang.termToBinary forwardTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right forwardBinary -> do+            send api0 forwardBinary+            return $ Right api0++forwardSync :: Typeable s => Instance.T s -> ByteString ->+    ByteString -> ByteString -> Int -> Int -> ByteString -> Source ->+    IO ()+forwardSync api0 name responseInfo response timeout priority transId pid = do+    result <- forwardSyncI api0+        name responseInfo response timeout priority transId pid+    case result of+        Left err ->+            error err+        Right api1 ->+            Exception.throwIO $ ForwardSync api1++return_ :: Typeable s =>+    Instance.T s -> Instance.RequestType -> ByteString -> ByteString ->+    ByteString -> ByteString -> Int -> ByteString -> Source ->+    IO ()+return_ api0 Instance.ASYNC = returnAsync api0+return_ api0 Instance.SYNC = returnSync api0++returnAsyncI :: Instance.T s -> ByteString -> ByteString ->+    ByteString -> ByteString -> Int -> ByteString -> Source ->+    IO (Result (Instance.T s))+returnAsyncI api0@Instance.T{+      Instance.requestTimeoutAdjustment = requestTimeoutAdjustment+    , Instance.requestTimer = requestTimer+    , Instance.requestTimeout = requestTimeout}+    name pattern responseInfo response timeout transId pid = do+    timeoutNew <- if requestTimeoutAdjustment && timeout == requestTimeout then+            timeoutAdjustment requestTimer timeout+        else+            return timeout+    let returnTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "return_async")+            , Erlang.OtpErlangString name+            , Erlang.OtpErlangString pattern+            , Erlang.OtpErlangBinary responseInfo+            , Erlang.OtpErlangBinary response+            , Erlang.OtpErlangInteger timeoutNew+            , Erlang.OtpErlangBinary transId+            , Erlang.OtpErlangPid pid]+    case Erlang.termToBinary returnTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right returnBinary -> do+            send api0 returnBinary+            return $ Right api0++returnAsync :: Typeable s => Instance.T s -> ByteString -> ByteString ->+    ByteString -> ByteString -> Int -> ByteString -> Source ->+    IO ()+returnAsync api0 name pattern responseInfo response timeout transId pid = do+    result <- returnAsyncI api0+        name pattern responseInfo response timeout transId pid+    case result of+        Left err ->+            error err+        Right api1 ->+            Exception.throwIO $ ReturnAsync api1++returnSyncI :: Instance.T s -> ByteString -> ByteString ->+    ByteString -> ByteString -> Int -> ByteString -> Source ->+    IO (Result (Instance.T s))+returnSyncI api0@Instance.T{+      Instance.requestTimeoutAdjustment = requestTimeoutAdjustment+    , Instance.requestTimer = requestTimer+    , Instance.requestTimeout = requestTimeout}+    name pattern responseInfo response timeout transId pid = do+    timeoutNew <- if requestTimeoutAdjustment && timeout == requestTimeout then+            timeoutAdjustment requestTimer timeout+        else+            return timeout+    let returnTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "return_sync")+            , Erlang.OtpErlangString name+            , Erlang.OtpErlangString pattern+            , Erlang.OtpErlangBinary responseInfo+            , Erlang.OtpErlangBinary response+            , Erlang.OtpErlangInteger timeoutNew+            , Erlang.OtpErlangBinary transId+            , Erlang.OtpErlangPid pid]+    case Erlang.termToBinary returnTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right returnBinary -> do+            send api0 returnBinary+            return $ Right api0++returnSync :: Typeable s => Instance.T s -> ByteString -> ByteString ->+    ByteString -> ByteString -> Int -> ByteString -> Source ->+    IO ()+returnSync api0 name pattern responseInfo response timeout transId pid = do+    result <- returnSyncI api0+        name pattern responseInfo response timeout transId pid+    case result of+        Left err ->+            error err+        Right api1 ->+            Exception.throwIO $ ReturnSync api1++recvAsync :: Typeable s => Instance.T s ->+    Maybe Int -> Maybe ByteString -> Maybe Bool ->+    IO (Result (ByteString, ByteString, ByteString, Instance.T s))+recvAsync api0@Instance.T{Instance.timeoutSync = timeoutSync'}+    timeoutOpt transIdOpt consumeOpt =+    let timeout = fromMaybe timeoutSync' timeoutOpt+        transId = fromMaybe transIdNull transIdOpt+        consume = fromMaybe True consumeOpt+        recvAsyncTerms = Erlang.OtpErlangTuple+            [ Erlang.OtpErlangAtom (Char8.pack "recv_async")+            , Erlang.OtpErlangInteger timeout+            , Erlang.OtpErlangBinary transId+            , Erlang.OtpErlangAtomBool consume]+    in+    case Erlang.termToBinary recvAsyncTerms (-1) of+        Left err ->+            return $ Left $ show err+        Right recvAsyncBinary -> do+            send api0 recvAsyncBinary+            result <- pollRequest api0 (-1) False+            case result of+                Left err ->+                    return $ Left err+                Right (_, api1@Instance.T{+                      Instance.responseInfo = responseInfo+                    , Instance.response = response+                    , Instance.transId = transId'}) ->+                    return $ Right (responseInfo, response, transId', api1)++processIndex :: Instance.T s -> Int+processIndex Instance.T{Instance.processIndex = processIndex'} =+    processIndex'++processCount :: Instance.T s -> Int+processCount Instance.T{Instance.processCount = processCount'} =+    processCount'++processCountMax :: Instance.T s -> Int+processCountMax Instance.T{Instance.processCountMax = processCountMax'} =+    processCountMax'++processCountMin :: Instance.T s -> Int+processCountMin Instance.T{Instance.processCountMin = processCountMin'} =+    processCountMin'++prefix :: Instance.T s -> ByteString+prefix Instance.T{Instance.prefix = prefix'} =+    prefix'++timeoutInitialize :: Instance.T s -> Int+timeoutInitialize Instance.T{Instance.timeoutInitialize = timeoutInitialize'} =+    timeoutInitialize'++timeoutAsync :: Instance.T s -> Int+timeoutAsync Instance.T{Instance.timeoutAsync = timeoutAsync'} =+    timeoutAsync'++timeoutSync :: Instance.T s -> Int+timeoutSync Instance.T{Instance.timeoutSync = timeoutSync'} =+    timeoutSync'++timeoutTerminate :: Instance.T s -> Int+timeoutTerminate Instance.T{Instance.timeoutTerminate = timeoutTerminate'} =+    timeoutTerminate'++nullResponse :: RequestType -> ByteString -> ByteString ->+    ByteString -> ByteString -> Int -> Int -> ByteString -> Source ->+    s -> Instance.T s -> IO (Instance.Response s)+nullResponse _ _ _ _ _ _ _ _ _ state api0 =+    return $ Instance.Null (state, api0)++callback :: Typeable s => Instance.T s ->+    (RequestType, ByteString, ByteString, ByteString, ByteString,+     Int, Int, ByteString, Source) -> IO (Result (Instance.T s))+callback api0@Instance.T{+      Instance.state = state+    , Instance.callbacks = callbacks+    , Instance.requestTimeoutAdjustment = requestTimeoutAdjustment}+    (requestType, name, pattern, requestInfo, request,+     timeout, priority, transId, pid) = do+    api1 <- if requestTimeoutAdjustment then do+            requestTimer <- POSIX.getPOSIXTime+            return api0{+                  Instance.requestTimer = requestTimer+                , Instance.requestTimeout = timeout}+        else+            return api0+    let (callbackF, callbacksNew) = case Map.lookup pattern callbacks of+            Nothing ->+                (nullResponse, callbacks)+            Just functionQueue ->+                let f = Sequence.index functionQueue 0+                    functionQueueNew = (Sequence.|>)+                        (Sequence.drop 0 functionQueue) f+                in+                (f, Map.insert pattern functionQueueNew callbacks)+        api2 = api1{Instance.callbacks = callbacksNew}+        empty = ByteString.empty+    callbackResultValue <- Exception.try $ case requestType of+        Instance.ASYNC -> do+            callbackResultAsyncValue <- Exception.try $+                callbackF requestType name pattern+                requestInfo request timeout priority transId pid+                state api2+            case callbackResultAsyncValue of+                Left (ReturnSync api3) -> do+                    printException "Synchronous Call Return Invalid"+                    return $ Finished api3+                Left (ReturnAsync api3) ->+                    return $ Finished api3+                Left (ForwardSync api3) -> do+                    printException "Synchronous Call Forward Invalid"+                    return $ Finished api3+                Left (ForwardAsync api3) ->+                    return $ Finished api3+                Right (Instance.ResponseInfo (v0, v1, v2, v3)) ->+                    return $ ReturnI (v0, v1, v2, v3)+                Right (Instance.Response (v0, v1, v2)) ->+                    return $ ReturnI (empty, v0, v1, v2)+                Right (Instance.Forward (v0, v1, v2, v3, v4)) ->+                    return $ ForwardI (v0, v1, v2, timeout, priority, v3, v4)+                Right (Instance.Forward_ (v0, v1, v2, v3, v4, v5, v6)) ->+                    return $ ForwardI (v0, v1, v2, v3, v4, v5, v6)+                Right (Instance.Null (v0, v1)) ->+                    return $ ReturnI (empty, empty, v0, v1)+                Right (Instance.NullError (err, v0, v1)) -> do+                    printError err+                    return $ ReturnI (empty, empty, v0, v1)+        Instance.SYNC -> do+            callbackResultSyncValue <- Exception.try $+                callbackF requestType name pattern+                requestInfo request timeout priority transId pid+                state api2+            case callbackResultSyncValue of+                Left (ReturnSync api3) ->+                    return $ Finished api3+                Left (ReturnAsync api3) -> do+                    printException "Asynchronous Call Return Invalid"+                    return $ Finished api3+                Left (ForwardSync api3) ->+                    return $ Finished api3+                Left (ForwardAsync api3) -> do+                    printException "Asynchronous Call Forward Invalid"+                    return $ Finished api3+                Right (Instance.ResponseInfo (v0, v1, v2, v3)) ->+                    return $ ReturnI (v0, v1, v2, v3)+                Right (Instance.Response (v0, v1, v2)) ->+                    return $ ReturnI (empty, v0, v1, v2)+                Right (Instance.Forward (v0, v1, v2, v3, v4)) ->+                    return $ ForwardI (v0, v1, v2, timeout, priority, v3, v4)+                Right (Instance.Forward_ (v0, v1, v2, v3, v4, v5, v6)) ->+                    return $ ForwardI (v0, v1, v2, v3, v4, v5, v6)+                Right (Instance.Null (v0, v1)) ->+                    return $ ReturnI (empty, empty, v0, v1)+                Right (Instance.NullError (err, v0, v1)) -> do+                    printError err+                    return $ ReturnI (empty, empty, v0, v1)+    callbackResultType <- case callbackResultValue of+        Left exception -> do+            printException $ show (exception :: Exception.SomeException)+            return $ Finished api2+        Right callbackResult ->+            return $ callbackResult+    case requestType of+        Instance.ASYNC ->+            case callbackResultType of+                Finished api4 ->+                    return $ Right api4+                ReturnI (responseInfo, response, state', api4) ->+                    returnAsyncI api4{Instance.state = state'}+                        name pattern responseInfo response timeout transId pid+                ForwardI (name', requestInfo', request', timeout', priority',+                          state', api4) ->+                    forwardAsyncI api4{Instance.state = state'}+                        name' requestInfo' request'+                        timeout' priority' transId pid+        Instance.SYNC ->+            case callbackResultType of+                Finished api4 ->+                    return $ Right api4+                ReturnI (responseInfo, response, state', api4) ->+                    returnSyncI api4{Instance.state = state'}+                        name pattern responseInfo response timeout transId pid+                ForwardI (name', requestInfo', request', timeout', priority',+                          state', api4) ->+                    forwardSyncI api4{Instance.state = state'}+                        name' requestInfo' request'+                        timeout' priority' transId pid++handleEvents :: [Message] -> Instance.T s -> Bool -> Word32 ->+    Get ([Message], Instance.T s)+handleEvents messages api0 external cmd0 = do+    cmd <- if cmd0 == 0 then Get.getWord32host else return cmd0+    case () of+      _ | cmd == messageTerm ->+            let api1 = api0{+                      Instance.terminate = True+                    , Instance.timeout = Just False} in+            if external then+                return ([], api1)+            else+                fail terminateError+        | cmd == messageReinit -> do+            processCount' <- Get.getWord32host+            timeoutAsync' <- Get.getWord32host+            timeoutSync' <- Get.getWord32host+            priorityDefault <- Get.getInt8+            requestTimeoutAdjustment <- Get.getWord8+            let api1 = Instance.reinit api0+                    processCount'+                    timeoutAsync'+                    timeoutSync'+                    priorityDefault+                    requestTimeoutAdjustment+            empty <- Get.isEmpty+            if not empty then+                handleEvents messages api1 external 0+            else+                return (messages, api1)+        | cmd == messageKeepalive -> do+            let messagesNew = MessageKeepalive:messages+            empty <- Get.isEmpty+            if not empty then+                handleEvents messagesNew api0 external 0+            else+                return (messagesNew, api0)+        | otherwise ->+            fail messageDecodingError++pollRequestDataGet :: [Message] -> Instance.T s -> Bool ->+    Get ([Message], Instance.T s)+pollRequestDataGet messages api0 external = do+    cmd <- Get.getWord32host+    case () of+      _ | cmd == messageInit -> do+            processIndex' <- Get.getWord32host+            processCount' <- Get.getWord32host+            processCountMax' <- Get.getWord32host+            processCountMin' <- Get.getWord32host+            prefixSize <- Get.getWord32host+            prefix' <- Get.getByteString $ fromIntegral prefixSize - 1+            Get.skip 1+            timeoutInitialize' <- Get.getWord32host+            timeoutAsync' <- Get.getWord32host+            timeoutSync' <- Get.getWord32host+            timeoutTerminate' <- Get.getWord32host+            priorityDefault <- Get.getInt8+            requestTimeoutAdjustment <- Get.getWord8+            let api1 = Instance.init api0+                    processIndex'+                    processCount'+                    processCountMax'+                    processCountMin'+                    prefix'+                    timeoutInitialize'+                    timeoutAsync'+                    timeoutSync'+                    timeoutTerminate'+                    priorityDefault+                    requestTimeoutAdjustment+            empty <- Get.isEmpty+            if not empty then+                handleEvents messages api1 external 0+            else+                return (messages, api1)+        | cmd == messageSendAsync || cmd == messageSendSync -> do+            nameSize <- Get.getWord32host+            name <- Get.getByteString $ fromIntegral nameSize - 1+            Get.skip 1+            patternSize <- Get.getWord32host+            pattern <- Get.getByteString $ fromIntegral patternSize - 1+            Get.skip 1+            requestInfoSize <- Get.getWord32host+            requestInfo <- Get.getByteString $ fromIntegral requestInfoSize+            Get.skip 1+            requestSize <- Get.getWord32host+            request <- Get.getByteString $ fromIntegral requestSize+            Get.skip 1+            timeout <- Get.getWord32host+            priority <- Get.getInt8+            transId <- Get.getByteString 16+            pidSize <- Get.getWord32host+            pidData <- Get.getLazyByteString $ fromIntegral pidSize+            empty <- Get.isEmpty+            case Erlang.binaryToTerm pidData of+                Left err ->+                    fail $ show err+                Right (Erlang.OtpErlangPid (pid)) ->+                    let requestType =+                            if cmd == messageSendAsync then+                                Instance.ASYNC+                            else -- cmd == messageSendSync+                                Instance.SYNC+                        messagesNew = (MessageSend (+                            requestType, name, pattern, requestInfo, request,+                            fromIntegral timeout, fromIntegral priority,+                            transId, pid)):messages in+                    if not empty then+                        handleEvents messagesNew api0 external 0+                    else+                        return (messagesNew, api0)+                Right _ ->+                    fail messageDecodingError+        | cmd == messageRecvAsync || cmd == messageReturnSync -> do+            responseInfoSize <- Get.getWord32host+            responseInfo <- Get.getByteString $ fromIntegral responseInfoSize+            responseSize <- Get.getWord32host+            response <- Get.getByteString $ fromIntegral responseSize+            transId <- Get.getByteString 16+            empty <- Get.isEmpty+            let api1 = Instance.setResponse api0+                    responseInfo response transId+            if not empty then+                handleEvents messages api1 external 0+            else+                return (messages, api1)+        | cmd == messageReturnAsync -> do+            transId <- Get.getByteString 16+            empty <- Get.isEmpty+            let api1 = Instance.setTransId api0+                    transId+            if not empty then+                handleEvents messages api1 external 0+            else+                return (messages, api1)+        | cmd == messageReturnsAsync -> do+            transIdCount <- Get.getWord32host+            transIds <- Get.getByteString $ 16 * (fromIntegral transIdCount)+            empty <- Get.isEmpty+            let api1 = Instance.setTransIds api0+                    transIds transIdCount+            if not empty then+                handleEvents messages api1 external 0+            else+                return (messages, api1)+        | cmd == messageSubscribeCount -> do+            subscribeCount' <- Get.getWord32host+            empty <- Get.isEmpty+            let api1 = Instance.setSubscribeCount api0+                    subscribeCount'+            if not empty then+                handleEvents messages api1 external 0+            else+                return (messages, api1)+        | cmd == messageTerm ->+            handleEvents messages api0 external cmd+        | cmd == messageReinit -> do+            processCount' <- Get.getWord32host+            timeoutAsync' <- Get.getWord32host+            timeoutSync' <- Get.getWord32host+            priorityDefault <- Get.getInt8+            requestTimeoutAdjustment <- Get.getWord8+            let api1 = Instance.reinit api0+                    processCount'+                    timeoutAsync'+                    timeoutSync'+                    priorityDefault+                    requestTimeoutAdjustment+            empty <- Get.isEmpty+            if not empty then+                pollRequestDataGet messages api1 external+            else+                return (messages, api1)+        | cmd == messageKeepalive -> do+            let messagesNew = MessageKeepalive:messages+            empty <- Get.isEmpty+            if not empty then+                pollRequestDataGet messagesNew api0 external+            else+                return (messagesNew, api0)+        | otherwise ->+            fail messageDecodingError++pollRequestDataProcess :: Typeable s => [Message] -> Instance.T s ->+    IO (Result (Instance.T s))+pollRequestDataProcess [] api0 =+    return $ Right api0+pollRequestDataProcess (message:messages) api0 =+    case message of+        MessageSend callbackData -> do+            callbackResult <- callback api0 callbackData+            case callbackResult of+                Left err ->+                    return $ Left err+                Right api1 ->+                    pollRequestDataProcess messages api1+        MessageKeepalive ->+            let aliveTerms = Erlang.OtpErlangAtom (Char8.pack "keepalive") in+            case Erlang.termToBinary aliveTerms (-1) of+                Left err ->+                    return $ Left $ show err+                Right aliveBinary -> do+                    send api0 aliveBinary+                    pollRequestDataProcess messages api0++pollRequestData :: Typeable s => Instance.T s -> Bool -> LazyByteString ->+    IO (Result (Instance.T s))+pollRequestData api0 external dataIn =+    case Get.runGetOrFail (pollRequestDataGet [] api0 external) dataIn of+        Left (_, _, err) ->+            return $ Left err+        Right (_, _, (messages, api1)) ->+            pollRequestDataProcess (List.reverse messages) api1++pollRequestLoop :: Typeable s =>+    Instance.T s -> Int -> Bool -> Clock.NominalDiffTime ->+    IO (Result (Bool, Instance.T s))+pollRequestLoop api0@Instance.T{+      Instance.socketHandle = socketHandle+    , Instance.bufferRecvSize = bufferRecvSize}+    timeout external pollTimer = do+    inputAvailable <- if bufferRecvSize > 0 then+            return True+        else+            SysIO.hWaitForInput socketHandle timeout+    if not inputAvailable then+        return $ Right (True, api0{Instance.timeout = Nothing})+    else do+        (dataIn, _, api1) <- recv api0+        dataResult <- pollRequestData api1 external dataIn+        case dataResult of+            Left err ->+                return $ Left err+            Right api2@Instance.T{Instance.timeout = Just result} ->+                return $ Right (result, api2{Instance.timeout = Nothing})+            Right api2 -> do+                (pollTimerNew, timeoutNew) <- if timeout <= 0 then+                        return (0, timeout)+                    else+                        timeoutAdjustmentPoll pollTimer timeout+                if timeout == 0 then+                    return $ Right (True, api2{Instance.timeout = Nothing})+                else+                    pollRequestLoop api2 timeoutNew external pollTimerNew++pollRequestLoopBegin :: Typeable s =>+    Instance.T s -> Int -> Bool -> Clock.NominalDiffTime ->+    IO (Result (Bool, Instance.T s))+pollRequestLoopBegin api0 timeout external pollTimer = do+    result <- Exception.try (pollRequestLoop api0 timeout external pollTimer)+    case result of+        Left exception ->+            return $ Left $ show (exception :: SomeException)+        Right success ->+            return success++pollRequest :: Typeable s => Instance.T s -> Int -> Bool ->+    IO (Result (Bool, Instance.T s))+pollRequest api0@Instance.T{+      Instance.initializationComplete = initializationComplete+    , Instance.terminate = terminate} timeout external =+    if terminate then+        return $ Right (False, api0)+    else do+        pollTimer <- if timeout <= 0 then+                return 0+            else+                POSIX.getPOSIXTime+        if external && not initializationComplete then+            let pollingTerms = Erlang.OtpErlangAtom (Char8.pack "polling") in+            case Erlang.termToBinary pollingTerms (-1) of+                Left err ->+                    return $ Left $ show err+                Right pollingBinary -> do+                    send api0 pollingBinary+                    pollRequestLoopBegin+                        api0{Instance.initializationComplete = True}+                        timeout external pollTimer+        else+            pollRequestLoopBegin api0 timeout external pollTimer++poll :: Typeable s => Instance.T s -> Int -> IO (Result (Bool, Instance.T s))+poll api0 timeout =+    pollRequest api0 timeout True++send :: Instance.T s -> LazyByteString -> IO ()+send Instance.T{+      Instance.useHeader = True+    , Instance.socketHandle = socketHandle} binary = do+    let total = fromIntegral (LazyByteString.length binary) :: Word32+    LazyByteString.hPut socketHandle (Builder.toLazyByteString $+        Builder.word32BE total `Monoid.mappend`+        Builder.lazyByteString binary)+send Instance.T{+      Instance.useHeader = False+    , Instance.socketHandle = socketHandle} binary = do+    LazyByteString.hPut socketHandle binary++recvBuffer :: Builder -> Int -> Int -> Handle -> Int ->+    IO (Builder, Int)+recvBuffer bufferRecv bufferRecvSize recvSize socketHandle bufferSize+    | recvSize <= bufferRecvSize =+        return (bufferRecv, bufferRecvSize)+    | otherwise = do+        fragment <- ByteString.hGetNonBlocking socketHandle bufferSize+        recvBuffer+            (bufferRecv `Monoid.mappend` Builder.byteString fragment)+            (bufferRecvSize + ByteString.length fragment)+            recvSize socketHandle bufferSize++recvBufferAll :: Builder -> Int -> Handle -> Int ->+    IO (Builder, Int)+recvBufferAll bufferRecv bufferRecvSize socketHandle bufferSize = do+    fragment <- ByteString.hGetNonBlocking socketHandle bufferSize+    let fragmentSize = ByteString.length fragment+        bufferRecvNew = bufferRecv `Monoid.mappend` Builder.byteString fragment+        bufferRecvSizeNew = bufferRecvSize + fragmentSize+    if fragmentSize == bufferSize then+        recvBufferAll bufferRecvNew bufferRecvSizeNew socketHandle bufferSize+    else+        return (bufferRecv, bufferRecvSize)++recv :: Instance.T s -> IO (LazyByteString, Int, Instance.T s)+recv api0@Instance.T{+      Instance.useHeader = True+    , Instance.socketHandle = socketHandle+    , Instance.bufferSize = bufferSize+    , Instance.bufferRecv = bufferRecv+    , Instance.bufferRecvSize = bufferRecvSize} = do+    (bufferRecvHeader, bufferRecvHeaderSize) <- recvBuffer+        bufferRecv bufferRecvSize 4 socketHandle bufferSize+    let header0 = Builder.toLazyByteString bufferRecvHeader+        Just (byte0, header1) = LazyByteString.uncons header0+        Just (byte1, header2) = LazyByteString.uncons header1+        Just (byte2, header3) = LazyByteString.uncons header2+        Just (byte3, headerRemaining) = LazyByteString.uncons header3+        total = fromIntegral $+            (fromIntegral byte0 :: Word32) `shiftL` 24 .|.+            (fromIntegral byte1 :: Word32) `shiftL` 16 .|.+            (fromIntegral byte2 :: Word32) `shiftL` 8 .|.+            (fromIntegral byte3 :: Word32) :: Int+    (bufferRecvAll, bufferRecvAllSize) <- recvBuffer+        (Builder.lazyByteString headerRemaining)+        (bufferRecvHeaderSize - 4) total socketHandle bufferSize+    let bufferRecvAllStr = Builder.toLazyByteString bufferRecvAll+        (bufferRecvData, bufferRecvNew) =+            LazyByteString.splitAt (fromIntegral total) bufferRecvAllStr+    return $ (+          bufferRecvData+        , total+        , api0{+              Instance.bufferRecv = Builder.lazyByteString bufferRecvNew+            , Instance.bufferRecvSize = bufferRecvAllSize - total})+recv api0@Instance.T{+      Instance.useHeader = False+    , Instance.socketHandle = socketHandle+    , Instance.bufferSize = bufferSize+    , Instance.bufferRecv = bufferRecv+    , Instance.bufferRecvSize = bufferRecvSize} = do+    (bufferRecvAll,+     bufferRecvAllSize) <- recvBufferAll+        bufferRecv bufferRecvSize socketHandle bufferSize+    return $ (+          Builder.toLazyByteString bufferRecvAll+        , bufferRecvAllSize+        , api0{+              Instance.bufferRecv = Monoid.mempty+            , Instance.bufferRecvSize = 0})++timeoutAdjustment :: Clock.NominalDiffTime -> Int ->+    IO (Int)+timeoutAdjustment t0 timeout = do+    t1 <- POSIX.getPOSIXTime+    if t1 <= t0 then+        return timeout+    else+        let elapsed = floor ((t1 - t0) * 1000) :: Integer+            timeoutValue = fromIntegral timeout :: Integer in+        if elapsed >= timeoutValue then+            return 0+        else+            return $ fromIntegral $ timeoutValue - elapsed++timeoutAdjustmentPoll :: Clock.NominalDiffTime -> Int ->+    IO (Clock.NominalDiffTime, Int)+timeoutAdjustmentPoll t0 timeout = do+    t1 <- POSIX.getPOSIXTime+    if t1 <= t0 then+        return (t1, timeout)+    else+        let elapsed = floor ((t1 - t0) * 1000) :: Integer+            timeoutValue = fromIntegral timeout :: Integer in+        if elapsed >= timeoutValue then+            return (t1, 0)+        else+            return (t1, fromIntegral $ timeoutValue - elapsed)++threadList :: Concurrent.MVar [Concurrent.MVar ()]+threadList = Unsafe.unsafePerformIO (Concurrent.newMVar [])++threadCreate :: (Int -> IO ()) -> Int -> IO ThreadId+threadCreate f threadIndex = do+    thread <- Concurrent.newEmptyMVar+    threads <- Concurrent.takeMVar threadList+    Concurrent.putMVar threadList (thread:threads)+    threadCreateFork threadIndex (f threadIndex)+        (\_ -> Concurrent.putMVar thread ())++threadCreateFork :: Int -> IO a -> (Either SomeException a -> IO ()) ->+    IO ThreadId+threadCreateFork threadIndex action afterF =+    -- similar to Concurrent.forkFinally+    Exception.mask $ \restore ->+        Concurrent.forkOn threadIndex+            (Exception.try (restore action) >>= afterF)++threadsWait :: IO ()+threadsWait = do+    threads <- Concurrent.takeMVar threadList+    case threads of+        [] ->+            return ()+        done:remaining -> do+            Concurrent.putMVar threadList remaining+            Concurrent.takeMVar done+            threadsWait++textKeyValueParse :: ByteString -> Map ByteString [ByteString]+textKeyValueParse text =+    let loop m [] = m+        loop m [v] =+            if v == ByteString.empty then+                m+            else+                error "not text_pairs"+        loop m (k:(v:l')) =+            case Map.lookup k m of+                Nothing ->+                    loop (Map.insert k [v] m) l'+                Just v' ->+                    loop (Map.insert k (v:v') m) l'+    in+    loop Map.empty (Char8.split '\0' text)++infoKeyValueParse :: ByteString -> Map ByteString [ByteString]+infoKeyValueParse = textKeyValueParse+
+ src/Foreign/CloudI/Instance.hs view
@@ -0,0 +1,296 @@+{--*-Mode:haskell;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-+  ex: set ft=haskell fenc=utf-8 sts=4 ts=4 sw=4 et nomod: -}++{-++  BSD LICENSE++  Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>+  All rights reserved.++  Redistribution and use in source and binary forms, with or without+  modification, are permitted provided that the following conditions are met:++      * Redistributions of source code must retain the above copyright+        notice, this list of conditions and the following disclaimer.+      * Redistributions in binary form must reproduce the above copyright+        notice, this list of conditions and the following disclaimer in+        the documentation and/or other materials provided with the+        distribution.+      * All advertising materials mentioning features or use of this+        software must display the following acknowledgment:+          This product includes software developed by Michael Truog+      * The name of the author may not be used to endorse or promote+        products derived from this software without specific prior+        written permission++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+  DAMAGE.++ -}++module Foreign.CloudI.Instance+    ( RequestType(..)+    , Source+    , Response(..)+    , Callback+    , T(..)+    , make+    , init+    , reinit+    , setResponse+    , setTransId+    , setTransIds+    , setSubscribeCount+    , callbacksAdd+    , callbacksRemove+    ) where++import Prelude hiding (init)+import Data.Typeable (Typeable)+import qualified Data.Array.IArray as IArray+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Builder as Builder+import qualified Data.Int as Int+import qualified Data.Map.Strict as Map+import qualified Data.Monoid as Monoid+import qualified Data.Sequence as Sequence+import qualified Data.Time.Clock as Clock+import qualified Data.Word as Word+import qualified Foreign.C.Types as C+import qualified Foreign.Erlang.Pid as Erlang+import qualified Network.Socket as Socket+import qualified System.IO as SysIO+type Array = IArray.Array+type Builder = Builder.Builder+type ByteString = ByteString.ByteString+type Handle = SysIO.Handle+type Int8 = Int.Int8+type Map = Map.Map+type Seq = Sequence.Seq+type Socket = Socket.Socket+type Word8 = Word.Word8+type Word32 = Word.Word32++data RequestType =+      ASYNC+    | SYNC+    deriving (Eq, Show)++type Source = Erlang.Pid++type Callback s =+    RequestType ->+    ByteString -> ByteString ->+    ByteString -> ByteString ->+    Int -> Int -> ByteString -> Source ->+    s -> T s ->+    IO (Response s)++data Response s =+      Response (ByteString, s, T s)+    | ResponseInfo (ByteString, ByteString, s, T s)+    | Forward (ByteString, ByteString, ByteString, s, T s)+    | Forward_ (ByteString, ByteString, ByteString, Int, Int, s, T s)+    | Null (s, T s)+    | NullError (String, s, T s)+    deriving (Show, Typeable)++data T s = T+    { state :: !s+    , socketHandle :: !Handle+    , useHeader :: !Bool+    , initializationComplete :: !Bool+    , terminate :: !Bool+    , timeout :: !(Maybe Bool)+    , callbacks :: !(Map ByteString (Seq (Callback s)))+    , bufferSize :: !Int+    , bufferRecv :: !Builder+    , bufferRecvSize :: !Int+    , processIndex :: !Int+    , processCount :: !Int+    , processCountMax :: !Int+    , processCountMin :: !Int+    , prefix :: !ByteString+    , timeoutInitialize :: !Int+    , timeoutAsync :: !Int+    , timeoutSync :: !Int+    , timeoutTerminate :: !Int+    , priorityDefault :: !Int+    , requestTimeoutAdjustment :: !Bool+    , requestTimer :: !Clock.NominalDiffTime+    , requestTimeout :: !Int+    , responseInfo :: !ByteString+    , response :: !ByteString+    , transId :: !ByteString+    , transIds :: !(Array Int ByteString)+    , subscribeCount :: !Int+    }+    deriving (Typeable)++instance Show (T s) where+    show _ = ""++makeSocket :: String -> C.CInt -> IO Socket+makeSocket "local" fd =+    Socket.mkSocket fd Socket.AF_UNIX Socket.Stream+        Socket.defaultProtocol Socket.Connected+makeSocket "tcp" fd =+    Socket.mkSocket fd Socket.AF_INET Socket.Stream+        Socket.defaultProtocol Socket.Connected+makeSocket "udp" fd =+    Socket.mkSocket fd Socket.AF_INET Socket.Datagram+        Socket.defaultProtocol Socket.Connected+makeSocket _ _ =+    error "invalid protocol"++makeSocketHandle :: String -> C.CInt -> IO Handle+makeSocketHandle protocol fd = do+    socket <- makeSocket protocol fd+    Socket.socketToHandle socket SysIO.ReadWriteMode++make :: s -> String -> C.CInt -> Bool -> Int -> Int -> IO (T s)+make state' protocol fd useHeader' bufferSize' timeoutTerminate' = do+    socketHandle' <- makeSocketHandle protocol fd+    return $ T {+          state = state'+        , socketHandle = socketHandle'+        , useHeader = useHeader'+        , initializationComplete = False+        , terminate = False+        , timeout = Nothing+        , callbacks = Map.empty+        , bufferSize = bufferSize'+        , bufferRecv = Monoid.mempty+        , bufferRecvSize = 0+        , processIndex = 0+        , processCount = 0+        , processCountMax = 0+        , processCountMin = 0+        , prefix = ByteString.empty+        , timeoutInitialize = 0+        , timeoutAsync = 0+        , timeoutSync = 0+        , timeoutTerminate = timeoutTerminate'+        , priorityDefault = 0+        , requestTimeoutAdjustment = False+        , requestTimer = 0+        , requestTimeout = 0+        , responseInfo = ByteString.empty+        , response = ByteString.empty+        , transId = ByteString.empty+        , transIds = IArray.array (0, 0) [(0, ByteString.empty)]+        , subscribeCount = 0+    }++init :: T s -> Word32 -> Word32 -> Word32 -> Word32 -> ByteString ->+    Word32 -> Word32 -> Word32 -> Word32 -> Int8 -> Word8 -> T s+init api0+    processIndex' processCount' processCountMax' processCountMin'+    prefix' timeoutInitialize' timeoutAsync' timeoutSync' timeoutTerminate'+    priorityDefault' requestTimeoutAdjustment' =+    api0{+          timeout = Just False+        , processIndex = fromIntegral processIndex'+        , processCount = fromIntegral processCount'+        , processCountMax = fromIntegral processCountMax'+        , processCountMin = fromIntegral processCountMin'+        , prefix = prefix'+        , timeoutInitialize = fromIntegral timeoutInitialize'+        , timeoutAsync = fromIntegral timeoutAsync'+        , timeoutSync = fromIntegral timeoutSync'+        , timeoutTerminate = fromIntegral timeoutTerminate'+        , priorityDefault = fromIntegral priorityDefault'+        , requestTimeoutAdjustment = requestTimeoutAdjustment' /= 0}++reinit :: T s -> Word32 -> Word32 -> Word32 -> Int8 -> Word8 -> T s+reinit api0+    processCount' timeoutAsync' timeoutSync'+    priorityDefault' requestTimeoutAdjustment' =+    api0{+          processCount = fromIntegral processCount'+        , timeoutAsync = fromIntegral timeoutAsync'+        , timeoutSync = fromIntegral timeoutSync'+        , priorityDefault = fromIntegral priorityDefault'+        , requestTimeoutAdjustment = requestTimeoutAdjustment' /= 0}++setResponse :: T s -> ByteString -> ByteString -> ByteString -> T s+setResponse api0+    responseInfo' response' transId' =+    api0{+          timeout = Just False+        , responseInfo = responseInfo'+        , response = response'+        , transId = transId'}++setTransId :: T s -> ByteString -> T s+setTransId api0+    transId' =+    api0{+          timeout = Just False+        , transId = transId'}++setTransIds :: T s -> ByteString -> Word32 -> T s+setTransIds api0+    transIds' transIdCount =+    let count = fromIntegral transIdCount :: Int+        loop i l s =+            if i == count then+                l+            else+                let (e, s') = ByteString.splitAt 16 s in+                loop (i + 1) ((i, e):l) s'+    in+    api0{+          timeout = Just False+        , transIds = IArray.array (0, count - 1) (loop 0 [] transIds')}++setSubscribeCount :: T s -> Word32 -> T s+setSubscribeCount api0+    subscribeCount' =+    api0{+          timeout = Just False+        , subscribeCount = fromIntegral subscribeCount'}++callbacksAdd :: T s -> ByteString -> Callback s -> T s+callbacksAdd api0@T{+      callbacks = callbacks0+    , prefix = prefix'} pattern f =+    let key = ByteString.append prefix' pattern+        callbacks1 = case Map.lookup key callbacks0 of+            Nothing ->+                Map.insert key (Sequence.singleton f) callbacks0+            Just functionQueue ->+                Map.insert key ((Sequence.|>) functionQueue f) callbacks0+    in+    api0{callbacks = callbacks1}++callbacksRemove :: T s -> ByteString -> T s+callbacksRemove api0@T{+      callbacks = callbacks0+    , prefix = prefix'} pattern =+    let key = ByteString.append prefix' pattern+        callbacks1 = case Map.lookup key callbacks0 of+            Nothing ->+                error "callbacks empty"+            Just functionQueue ->+                let functionQueueNew = Sequence.drop 0 functionQueue in+                if Sequence.null functionQueueNew then+                    Map.delete key callbacks0+                else+                    Map.insert key functionQueueNew callbacks0+    in+    api0{callbacks = callbacks1}+
+ src/Foreign/Erlang.hs view
@@ -0,0 +1,728 @@+{--*-Mode:haskell;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-+  ex: set ft=haskell fenc=utf-8 sts=4 ts=4 sw=4 et nomod: -}++{-++  BSD LICENSE++  Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>+  All rights reserved.++  Redistribution and use in source and binary forms, with or without+  modification, are permitted provided that the following conditions are met:++      * Redistributions of source code must retain the above copyright+        notice, this list of conditions and the following disclaimer.+      * Redistributions in binary form must reproduce the above copyright+        notice, this list of conditions and the following disclaimer in+        the documentation and/or other materials provided with the+        distribution.+      * All advertising materials mentioning features or use of this+        software must display the following acknowledgment:+          This product includes software developed by Michael Truog+      * The name of the author may not be used to endorse or promote+        products derived from this software without specific prior+        written permission++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+  DAMAGE.++ -}++module Foreign.Erlang+    ( OtpErlangTerm(..)+    , Error(..)+    , Result+    , binaryToTerm+    , termToBinary+    ) where++import Prelude hiding (length,tail)+import Data.Bits ((.&.))+import Control.Monad (replicateM)+import qualified Data.Monoid as Monoid+import qualified Data.Binary.Get as Get+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Char8 as Char8+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.ByteString.Builder as Builder+import qualified Data.Int as DataInt+import qualified Data.List as List+import qualified Data.Map.Strict as Map+import qualified Data.Word as Word+import qualified Codec.Compression.Zlib as Zlib+import qualified Foreign.Erlang.Pid as E+import qualified Foreign.Erlang.Port as E+import qualified Foreign.Erlang.Reference as E+import qualified Foreign.Erlang.Function as E+type Get = Get.Get+type Builder = Builder.Builder+type ByteString = ByteString.ByteString+type LazyByteString = LazyByteString.ByteString+type Int32 = DataInt.Int32+type Map = Map.Map+type Word8 = Word.Word8+type Word16 = Word.Word16+type Word32 = Word.Word32+type Pid = E.Pid+type Port = E.Port+type Reference = E.Reference+type Function = E.Function++-- tag values here http://www.erlang.org/doc/apps/erts/erl_ext_dist.html+tagVersion :: Word8+tagVersion = 131+tagCompressedZlib :: Word8+tagCompressedZlib = 80+tagNewFloatExt :: Word8+tagNewFloatExt = 70+tagBitBinaryExt :: Word8+tagBitBinaryExt = 77+tagAtomCacheRef :: Word8+tagAtomCacheRef = 78+tagSmallIntegerExt :: Word8+tagSmallIntegerExt = 97+tagIntegerExt :: Word8+tagIntegerExt = 98+tagFloatExt :: Word8+tagFloatExt = 99+tagAtomExt :: Word8+tagAtomExt = 100+tagReferenceExt :: Word8+tagReferenceExt = 101+tagPortExt :: Word8+tagPortExt = 102+tagPidExt :: Word8+tagPidExt = 103+tagSmallTupleExt :: Word8+tagSmallTupleExt = 104+tagLargeTupleExt :: Word8+tagLargeTupleExt = 105+tagNilExt :: Word8+tagNilExt = 106+tagStringExt :: Word8+tagStringExt = 107+tagListExt :: Word8+tagListExt = 108+tagBinaryExt :: Word8+tagBinaryExt = 109+tagSmallBigExt :: Word8+tagSmallBigExt = 110+tagLargeBigExt :: Word8+tagLargeBigExt = 111+tagNewFunExt :: Word8+tagNewFunExt = 112+tagExportExt :: Word8+tagExportExt = 113+tagNewReferenceExt :: Word8+tagNewReferenceExt = 114+tagSmallAtomExt :: Word8+tagSmallAtomExt = 115+tagMapExt :: Word8+tagMapExt = 116+tagFunExt :: Word8+tagFunExt = 117+tagAtomUtf8Ext :: Word8+tagAtomUtf8Ext = 118+tagSmallAtomUtf8Ext :: Word8+tagSmallAtomUtf8Ext = 119++data OtpErlangTerm =+      OtpErlangInteger Int+    | OtpErlangIntegerBig Integer+    | OtpErlangFloat Double+    | OtpErlangAtom ByteString+    | OtpErlangAtomUTF8 ByteString+    | OtpErlangAtomCacheRef Int+    | OtpErlangAtomBool Bool+    | OtpErlangString ByteString+    | OtpErlangBinary ByteString+    | OtpErlangBinaryBits (ByteString, Int)+    | OtpErlangList [OtpErlangTerm]+    | OtpErlangListImproper [OtpErlangTerm]+    | OtpErlangTuple [OtpErlangTerm]+    | OtpErlangMap (Map OtpErlangTerm OtpErlangTerm)+    | OtpErlangPid Pid+    | OtpErlangPort Port+    | OtpErlangReference Reference+    | OtpErlangFunction Function+    deriving (Ord, Eq, Show)++data Error =+      InputError String+    | OutputError String+    | ParseError String+    deriving (Eq, Show)++type Result a = Either Error a++ok :: a -> Result a+ok value = Right value++errorType :: Error -> Result a+errorType value = Left value++getUnsignedInt8 :: Word8 -> Int+getUnsignedInt8 value = fromIntegral value++getUnsignedInt16 :: Word16 -> Int+getUnsignedInt16 value = fromIntegral value++getUnsignedInt32 :: Word32 -> Int+getUnsignedInt32 value = fromIntegral value++getSignedInt32 :: Word32 -> Int+getSignedInt32 value = fromIntegral (fromIntegral value :: Int32)++getUnsignedInt8or32 :: Bool -> Get Int+getUnsignedInt8or32 True = do+    value <- Get.getWord8+    return $ getUnsignedInt8 value+getUnsignedInt8or32 False = do+    value <- Get.getWord32be+    return $ getUnsignedInt32 value++boolTrue :: ByteString+boolTrue = Char8.pack "true"+boolFalse :: ByteString+boolFalse = Char8.pack "false"++infixr 4 <>+(<>) :: Monoid.Monoid m => m -> m -> m+(<>) = Monoid.mappend++binaryToTerm :: LazyByteString -> Result OtpErlangTerm+binaryToTerm binary =+    let size = LazyByteString.length binary in+    if size <= 1 then+        errorType $ ParseError "null input"+    else if LazyByteString.head binary /= tagVersion then+        errorType $ ParseError "invalid version"+    else+        case Get.runGetOrFail binaryToTerms (LazyByteString.tail binary) of+            Left (_, _, err) ->+                errorType $ ParseError err+            Right (_, _, term) ->+                ok term++termToBinary :: OtpErlangTerm -> Int -> Result LazyByteString+termToBinary term compressed+    | compressed < (-1) || compressed > 9 =+        errorType $ InputError "compressed in [-1..9]"+    | otherwise =+        case termsToBinary term of+            Left err ->+                errorType err+            Right dataUncompressed ->+                if compressed == (-1) then+                    ok $ LazyByteString.cons tagVersion dataUncompressed+                else+                    let sizeUncompressed =+                            LazyByteString.length dataUncompressed+                        params = Zlib.defaultCompressParams {+                            Zlib.compressLevel =+                            Zlib.CompressionLevel compressed }+                        dataCompressed =+                            Zlib.compressWith params dataUncompressed in+                    if sizeUncompressed > 4294967295 then+                        errorType $ OutputError "uint32 overflow"+                    else+                        ok $ Builder.toLazyByteString $+                            Builder.word8 tagVersion <>+                            Builder.word8 tagCompressedZlib <>+                            Builder.word32BE (fromIntegral sizeUncompressed) <>+                            Builder.lazyByteString dataCompressed++binaryToTerms :: Get OtpErlangTerm+binaryToTerms = do+    tag <- Get.getWord8+    case () of+      _ | tag == tagNewFloatExt -> do+            value <- Get.getDoublebe+            return $ OtpErlangFloat value+        | tag == tagBitBinaryExt -> do+            j <- Get.getWord32be+            bits <- Get.getWord8+            value <- Get.getByteString $ getUnsignedInt32 j+            return $ OtpErlangBinaryBits (value, getUnsignedInt8 bits)+        | tag == tagAtomCacheRef -> do+            value <- Get.getWord8+            return $ OtpErlangAtomCacheRef $ getUnsignedInt8 value+        | tag == tagSmallIntegerExt -> do+            value <- Get.getWord8+            return $ OtpErlangInteger $ getUnsignedInt8 value+        | tag == tagIntegerExt -> do+            value <- Get.getWord32be+            return $ OtpErlangInteger $ getSignedInt32 value+        | tag == tagFloatExt -> do+            str <- Get.getByteString 31+            let value = Char8.unpack $ Char8.takeWhile (\c -> c /= '\0') str+            return $ OtpErlangFloat (read value :: Double)+        | tag == tagAtomExt -> do+            j <- Get.getWord16be+            value <- Get.getByteString $ getUnsignedInt16 j+            return $ OtpErlangAtom value+        | tag == tagReferenceExt || tag == tagPortExt -> do+            (nodeTag, node) <- binaryToAtom+            eid <- Get.getByteString 4+            creation <- Get.getWord8+            if tag == tagReferenceExt then+                return $ OtpErlangReference $ E.Reference+                    nodeTag node eid creation+            else if tag == tagPortExt then+                return $ OtpErlangPort $ E.Port+                    nodeTag node eid creation+            else+                fail $ "invalid"+        | tag == tagPidExt -> do+            (nodeTag, node) <- binaryToAtom+            eid <- Get.getByteString 4+            serial <- Get.getByteString 4+            creation <- Get.getWord8+            return $ OtpErlangPid $ E.Pid+                nodeTag node eid serial creation+        | tag == tagSmallTupleExt || tag == tagLargeTupleExt -> do+            length <- getUnsignedInt8or32 $ tag == tagSmallTupleExt+            value <- binaryToTermSequence length+            return $ OtpErlangTuple value+        | tag == tagNilExt -> do+            return $ OtpErlangList []+        | tag == tagStringExt -> do+            j <- Get.getWord16be+            value <- Get.getByteString $ getUnsignedInt16 j+            return $ OtpErlangString value+        | tag == tagListExt -> do+            length <- Get.getWord32be+            tmp <- binaryToTermSequence $ getUnsignedInt32 length+            tail <- binaryToTerms+            if tail == OtpErlangList [] then+                return $ OtpErlangList tmp+            else+                return $ OtpErlangListImproper $ tmp ++ [tail]+        | tag == tagBinaryExt -> do+            j <- Get.getWord32be+            value <- Get.getByteString $ getUnsignedInt32 j+            return $ OtpErlangBinary value+        | tag == tagSmallBigExt || tag == tagLargeBigExt -> do+            j <- getUnsignedInt8or32 (tag == tagSmallBigExt)+            sign <- Get.getWord8+            digits <- replicateM j Get.getWord8+            let f = (\d -> \b -> b * 256 + (fromIntegral . getUnsignedInt8) d)+                value = List.foldr f (0 :: Integer) digits+            if sign == 1 then+                return $ OtpErlangIntegerBig $ (-1) * value+            else+                return $ OtpErlangIntegerBig value+        | tag == tagNewFunExt -> do+            length <- Get.getWord32be+            value <- Get.getByteString $ getUnsignedInt32 length+            return $ OtpErlangFunction $ E.Function+                tag value+        | tag == tagExportExt -> do+            length <- Get.lookAhead $ binaryToExportSize+            value <- Get.getByteString length+            return $ OtpErlangFunction $ E.Function+                tag value+        | tag == tagNewReferenceExt -> do+            j <- Get.getWord16be+            (nodeTag, node) <- binaryToAtom+            creation <- Get.getWord8+            eid <- Get.getByteString $ (getUnsignedInt16 j) * 4+            return $ OtpErlangReference $ E.Reference+                nodeTag node eid creation+        | tag == tagSmallAtomExt -> do+            j <- Get.getWord8+            value <- Get.getByteString $ getUnsignedInt8 j+            if value == boolTrue then+                return $ OtpErlangAtomBool True+            else if value == boolFalse then+                return $ OtpErlangAtomBool False+            else+                return $ OtpErlangAtom value+        | tag == tagMapExt -> do+            length <- Get.getWord32be+            pairs <- replicateM (getUnsignedInt32 length) binaryToMapPair+            return $ OtpErlangMap $ Map.fromList pairs+        | tag == tagFunExt -> do+            length <- Get.lookAhead $ binaryToFunSize+            value <- Get.getByteString length+            return $ OtpErlangFunction $ E.Function+                tag value+        | tag == tagAtomUtf8Ext -> do+            j <- Get.getWord16be+            value <- Get.getByteString $ getUnsignedInt16 j+            return $ OtpErlangAtomUTF8 value+        | tag == tagSmallAtomUtf8Ext -> do+            j <- Get.getWord8+            value <- Get.getByteString $ getUnsignedInt8 j+            return $ OtpErlangAtomUTF8 value+        | tag == tagCompressedZlib -> do+            sizeUncompressed <- Get.getWord32be+            compressed <- Get.getRemainingLazyByteString+            let dataUncompressed = Zlib.decompress $ compressed+                size1 = fromIntegral $ getUnsignedInt32 sizeUncompressed+                size2 = LazyByteString.length dataUncompressed+            if size1 == 0 || size1 /= size2 then+                fail $ "compression corrupt"+            else+                case Get.runGetOrFail binaryToTerms dataUncompressed of+                    Left (_, _, err) ->+                        fail err+                    Right (_, _, term) ->+                        return term+        | otherwise ->+            fail $ "invalid tag"++binaryToTermSequence :: Int -> Get [OtpErlangTerm]+binaryToTermSequence length = do+    value <- replicateM length binaryToTerms+    return value++binaryToMapPair :: Get (OtpErlangTerm, OtpErlangTerm)+binaryToMapPair = do+    key <- binaryToTerms+    value <- binaryToTerms+    return (key, value)++binaryToExportSize :: Get Int+binaryToExportSize = do+    oldI <- Get.bytesRead+    (_, _) <- binaryToAtom -- module+    (_, _) <- binaryToAtom -- function+    arityTag <- Get.getWord8+    _ <- Get.getWord8 -- arity+    i <- Get.bytesRead+    if arityTag == tagSmallIntegerExt then+        return $ fromIntegral (i - oldI)+    else+        fail $ "invalid small integer tag"++binaryToFunSize :: Get Int+binaryToFunSize = do+    oldI <- Get.bytesRead+    numfree <- Get.getWord32be+    _ <- binaryToPid -- pid+    (_, _) <- binaryToAtom -- module+    _ <- binaryToInteger -- index+    _ <- binaryToInteger -- uniq+    _ <- binaryToTermSequence (getUnsignedInt32 numfree) -- free+    i <- Get.bytesRead+    return $ fromIntegral (i - oldI)++binaryToInteger :: Get Int+binaryToInteger = do+    tag <- Get.getWord8+    case () of+      _ | tag == tagSmallIntegerExt -> do+            value <- Get.getWord8+            return $ getUnsignedInt8 value+        | tag == tagIntegerExt -> do+            value <- Get.getWord32be+            return $ getSignedInt32 value+        | otherwise ->+            fail $ "invalid integer tag"++binaryToPid :: Get Pid+binaryToPid = do+    tag <- Get.getWord8+    case () of+      _ | tag == tagPidExt -> do+            (nodeTag, node) <- binaryToAtom+            eid <- Get.getByteString 4+            serial <- Get.getByteString 4+            creation <- Get.getWord8+            return $ E.Pid+                nodeTag node eid serial creation+        | otherwise ->+            fail $ "invalid pid tag"++binaryToAtom :: Get (Word8, ByteString)+binaryToAtom = do+    tag <- Get.getWord8+    case () of+      _ | tag == tagAtomExt -> do+            j <- Get.lookAhead $ Get.getWord16be+            value <- Get.getByteString $ 2 + (getUnsignedInt16 j)+            return (tag, value)+        | tag == tagAtomCacheRef -> do+            value <- Get.getByteString 1+            return (tag, value)+        | tag == tagSmallAtomExt -> do+            j <- Get.lookAhead $ Get.getWord8+            value <- Get.getByteString $ 1 + (getUnsignedInt8 j)+            return (tag, value)+        | tag == tagAtomUtf8Ext -> do+            j <- Get.lookAhead $ Get.getWord16be+            value <- Get.getByteString $ 2 + (getUnsignedInt16 j)+            return (tag, value)+        | tag == tagSmallAtomUtf8Ext -> do+            j <- Get.lookAhead $ Get.getWord8+            value <- Get.getByteString $ 1 + (getUnsignedInt8 j)+            return (tag, value)+        | otherwise ->+            fail $ "invalid atom tag"++termsToBinary :: OtpErlangTerm -> Result LazyByteString+termsToBinary (OtpErlangInteger value)+    | value >= 0 && value <= 255 =+        ok $ Builder.toLazyByteString $+            Builder.word8 tagSmallIntegerExt <>+            Builder.word8 (fromIntegral value)+    | value >= (-2147483648) && value <= 2147483647 =+        ok $ Builder.toLazyByteString $+            Builder.word8 tagIntegerExt <>+            Builder.int32BE (fromIntegral value)+    | otherwise =+        termsToBinary $ OtpErlangIntegerBig $ fromIntegral value+termsToBinary (OtpErlangIntegerBig value) =+    let sign = if value < 0 then 1 else 0+        loop bignum l =+            if bignum > 0 then+                loop (bignum `quot` 256)+                    (LazyByteString.cons (fromIntegral $ bignum .&. 255) l)+            else+                LazyByteString.reverse l+        lResult = loop (abs value) LazyByteString.empty+        lLength = LazyByteString.length lResult in+    if lLength <= 255 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagSmallBigExt <>+            Builder.word8 (fromIntegral lLength) <>+            Builder.word8 sign <>+            Builder.lazyByteString lResult+    else if lLength <= 4294967295 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagLargeBigExt <>+            Builder.word32BE (fromIntegral lLength) <>+            Builder.word8 sign <>+            Builder.lazyByteString lResult+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangFloat value) =+    ok $ Builder.toLazyByteString $+        Builder.word8 tagNewFloatExt <>+        Builder.doubleBE value+termsToBinary (OtpErlangAtom value) =+    let length = ByteString.length value in+    if length <= 255 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagSmallAtomExt <>+            Builder.word8 (fromIntegral length) <>+            Builder.byteString value+    else if length <= 65535 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagAtomExt <>+            Builder.word16BE (fromIntegral length) <>+            Builder.byteString value+    else+        errorType $ OutputError "uint16 overflow"+termsToBinary (OtpErlangAtomUTF8 value) =+    let length = ByteString.length value in+    if length <= 255 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagSmallAtomUtf8Ext <>+            Builder.word8 (fromIntegral length) <>+            Builder.byteString value+    else if length <= 65535 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagAtomUtf8Ext <>+            Builder.word16BE (fromIntegral length) <>+            Builder.byteString value+    else+        errorType $ OutputError "uint16 overflow"+termsToBinary (OtpErlangAtomCacheRef value) =+    ok $ Builder.toLazyByteString $+        Builder.word8 tagAtomCacheRef <>+        Builder.word8 (fromIntegral value)+termsToBinary (OtpErlangAtomBool value) =+    if value then+        termsToBinary $ OtpErlangAtom $ Char8.pack "true"+    else+        termsToBinary $ OtpErlangAtom $ Char8.pack "false"+termsToBinary (OtpErlangString value) =+    let length = ByteString.length value in+    if length == 0 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagNilExt+    else if length <= 65535 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagStringExt <>+            Builder.word16BE (fromIntegral length) <>+            Builder.byteString value+    else if length <= 4294967295 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagListExt <>+            Builder.word32BE (fromIntegral length) <>+            Builder.word8 tagSmallIntegerExt <>+            Builder.byteString+                (ByteString.intersperse tagSmallIntegerExt value) <>+            Builder.word8 tagNilExt+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangBinary value) =+    let length = ByteString.length value in+    if length <= 4294967295 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagBinaryExt <>+            Builder.word32BE (fromIntegral length) <>+            Builder.byteString value+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangBinaryBits (value, 8)) =+    termsToBinary $ OtpErlangBinary value+termsToBinary (OtpErlangBinaryBits (value, bits)) =+    let length = ByteString.length value in+    if length <= 4294967295 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagBinaryExt <>+            Builder.word32BE (fromIntegral length) <>+            Builder.word8 (fromIntegral bits) <>+            Builder.byteString value+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangList value) =+    let length = List.length value in+    if length == 0 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagNilExt+    else if length <= 4294967295 then+        case termSequenceToBinary value Monoid.mempty of+            Left err ->+                errorType err+            Right listValue ->+                ok $ Builder.toLazyByteString $+                    Builder.word8 tagListExt <>+                    Builder.word32BE (fromIntegral length) <>+                    listValue <>+                    Builder.word8 tagNilExt+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangListImproper value) =+    let length = List.length value in+    if length == 0 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagNilExt -- misuse of OtpErlangListImproper+    else if length <= 4294967295 then+        case termSequenceToBinary value Monoid.mempty of+            Left err ->+                errorType err+            Right listValue ->+                ok $ Builder.toLazyByteString $+                    Builder.word8 tagListExt <>+                    Builder.word32BE (fromIntegral $ length - 1) <>+                    listValue+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangTuple value) =+    let length = List.length value in+    if length <= 255 then+        case termSequenceToBinary value Monoid.mempty of+            Left err ->+                errorType err+            Right tupleValue ->+                ok $ Builder.toLazyByteString $+                    Builder.word8 tagSmallTupleExt <>+                    Builder.word8 (fromIntegral length) <>+                    tupleValue+    else if length <= 4294967295 then+        case termSequenceToBinary value Monoid.mempty of+            Left err ->+                errorType err+            Right tupleValue ->+                ok $ Builder.toLazyByteString $+                    Builder.word8 tagLargeTupleExt <>+                    Builder.word32BE (fromIntegral length) <>+                    tupleValue+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangMap value) =+    let length = Map.size value in+    if length <= 4294967295 then+        case Map.foldlWithKey mapPairToBinary (Right Monoid.mempty) value of+            Left err ->+                errorType err+            Right mapValue ->+                ok $ Builder.toLazyByteString $+                    Builder.word8 tagMapExt <>+                    Builder.word32BE (fromIntegral length) <>+                    mapValue+    else+        errorType $ OutputError "uint32 overflow"+termsToBinary (OtpErlangPid (E.Pid nodeTag node eid serial creation)) =+    ok $ Builder.toLazyByteString $+        Builder.word8 tagPidExt <>+        Builder.word8 nodeTag <>+        Builder.byteString node <>+        Builder.byteString eid <>+        Builder.byteString serial <>+        Builder.word8 creation+termsToBinary (OtpErlangPort (E.Port nodeTag node eid creation)) =+    ok $ Builder.toLazyByteString $+        Builder.word8 tagPortExt <>+        Builder.word8 nodeTag <>+        Builder.byteString node <>+        Builder.byteString eid <>+        Builder.word8 creation+termsToBinary (OtpErlangReference (E.Reference nodeTag node eid creation)) =+    let length = (ByteString.length eid) `quot` 4 in+    if length == 0 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagReferenceExt <>+            Builder.word8 nodeTag <>+            Builder.byteString node <>+            Builder.byteString eid <>+            Builder.word8 creation+    else if length <= 65535 then+        ok $ Builder.toLazyByteString $+            Builder.word8 tagNewReferenceExt <>+            Builder.word16BE (fromIntegral length) <>+            Builder.word8 nodeTag <>+            Builder.byteString node <>+            Builder.word8 creation <>+            Builder.byteString eid+    else+        errorType $ OutputError "uint16 overflow"+termsToBinary (OtpErlangFunction (E.Function tag value)) =+    ok $ Builder.toLazyByteString $+        Builder.word8 tag <>+        Builder.byteString value++termSequenceToBinary :: [OtpErlangTerm] -> Builder -> Result Builder+termSequenceToBinary [] builder =+    ok builder+termSequenceToBinary (h:t) builder =+    case termsToBinary h of+        Left err ->+            errorType err+        Right binary ->+            termSequenceToBinary t (builder <> Builder.lazyByteString binary)++mapPairToBinary :: Result Builder -> OtpErlangTerm -> OtpErlangTerm ->+    Result Builder+mapPairToBinary (Left err) _ _ =+    errorType err+mapPairToBinary (Right builder) key value =+    case termsToBinary key of+        Left err ->+            errorType err+        Right binaryKey ->+            case termsToBinary value of+                Left err ->+                    errorType err+                Right binaryValue ->+                    ok $ builder <>+                        Builder.lazyByteString binaryKey <>+                        Builder.lazyByteString binaryValue+
+ src/Foreign/Erlang/Function.hs view
@@ -0,0 +1,56 @@+{--*-Mode:haskell;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-+  ex: set ft=haskell fenc=utf-8 sts=4 ts=4 sw=4 et nomod: -}++{-++  BSD LICENSE++  Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>+  All rights reserved.++  Redistribution and use in source and binary forms, with or without+  modification, are permitted provided that the following conditions are met:++      * Redistributions of source code must retain the above copyright+        notice, this list of conditions and the following disclaimer.+      * Redistributions in binary form must reproduce the above copyright+        notice, this list of conditions and the following disclaimer in+        the documentation and/or other materials provided with the+        distribution.+      * All advertising materials mentioning features or use of this+        software must display the following acknowledgment:+          This product includes software developed by Michael Truog+      * The name of the author may not be used to endorse or promote+        products derived from this software without specific prior+        written permission++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+  DAMAGE.++ -}++module Foreign.Erlang.Function+    ( Function(..)+    ) where++import qualified Data.ByteString as ByteString+import qualified Data.Word as Word+type ByteString = ByteString.ByteString+type Word8 = Word.Word8++data Function = Function+    { tag :: !Word8+    , value :: !ByteString+    } deriving (Ord, Eq, Show)
+ src/Foreign/Erlang/Pid.hs view
@@ -0,0 +1,61 @@+{--*-Mode:haskell;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-+  ex: set ft=haskell fenc=utf-8 sts=4 ts=4 sw=4 et nomod: -}++{-++  BSD LICENSE++  Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>+  All rights reserved.++  Redistribution and use in source and binary forms, with or without+  modification, are permitted provided that the following conditions are met:++      * Redistributions of source code must retain the above copyright+        notice, this list of conditions and the following disclaimer.+      * Redistributions in binary form must reproduce the above copyright+        notice, this list of conditions and the following disclaimer in+        the documentation and/or other materials provided with the+        distribution.+      * All advertising materials mentioning features or use of this+        software must display the following acknowledgment:+          This product includes software developed by Michael Truog+      * The name of the author may not be used to endorse or promote+        products derived from this software without specific prior+        written permission++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+  DAMAGE.++ -}++module Foreign.Erlang.Pid+    ( Pid(..)+    ) where++import Prelude hiding (id)+import qualified Data.ByteString as ByteString+import qualified Data.Word as Word+type ByteString = ByteString.ByteString+type Word8 = Word.Word8++data Pid = Pid+    { nodeTag :: !Word8+    , node :: !ByteString+    , id :: !ByteString+    , serial :: !ByteString+    , creation :: !Word8+    } deriving (Ord, Eq, Show)+
+ src/Foreign/Erlang/Port.hs view
@@ -0,0 +1,60 @@+{--*-Mode:haskell;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-+  ex: set ft=haskell fenc=utf-8 sts=4 ts=4 sw=4 et nomod: -}++{-++  BSD LICENSE++  Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>+  All rights reserved.++  Redistribution and use in source and binary forms, with or without+  modification, are permitted provided that the following conditions are met:++      * Redistributions of source code must retain the above copyright+        notice, this list of conditions and the following disclaimer.+      * Redistributions in binary form must reproduce the above copyright+        notice, this list of conditions and the following disclaimer in+        the documentation and/or other materials provided with the+        distribution.+      * All advertising materials mentioning features or use of this+        software must display the following acknowledgment:+          This product includes software developed by Michael Truog+      * The name of the author may not be used to endorse or promote+        products derived from this software without specific prior+        written permission++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+  DAMAGE.++ -}++module Foreign.Erlang.Port+    ( Port(..)+    ) where++import Prelude hiding (id)+import qualified Data.ByteString as ByteString+import qualified Data.Word as Word+type ByteString = ByteString.ByteString+type Word8 = Word.Word8++data Port = Port+    { nodeTag :: !Word8+    , node :: !ByteString+    , id :: !ByteString+    , creation :: !Word8+    } deriving (Ord, Eq, Show)+
+ src/Foreign/Erlang/Reference.hs view
@@ -0,0 +1,60 @@+{--*-Mode:haskell;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-+  ex: set ft=haskell fenc=utf-8 sts=4 ts=4 sw=4 et nomod: -}++{-++  BSD LICENSE++  Copyright (c) 2017, Michael Truog <mjtruog at gmail dot com>+  All rights reserved.++  Redistribution and use in source and binary forms, with or without+  modification, are permitted provided that the following conditions are met:++      * Redistributions of source code must retain the above copyright+        notice, this list of conditions and the following disclaimer.+      * Redistributions in binary form must reproduce the above copyright+        notice, this list of conditions and the following disclaimer in+        the documentation and/or other materials provided with the+        distribution.+      * All advertising materials mentioning features or use of this+        software must display the following acknowledgment:+          This product includes software developed by Michael Truog+      * The name of the author may not be used to endorse or promote+        products derived from this software without specific prior+        written permission++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+  DAMAGE.++ -}++module Foreign.Erlang.Reference+    ( Reference(..)+    ) where++import Prelude hiding (id)+import qualified Data.ByteString as ByteString+import qualified Data.Word as Word+type ByteString = ByteString.ByteString+type Word8 = Word.Word8++data Reference = Reference+    { nodeTag :: !Word8+    , node :: !ByteString+    , id :: !ByteString+    , creation :: !Word8+    } deriving (Ord, Eq, Show)+