BufferedSocket 0.1.1.0 → 0.2.0.0
raw patch · 9 files changed
+835/−835 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Core: closeRead :: BufferedSocket -> IO ()
- Core: closeWrite :: BufferedSocket -> IO ()
- Core: data BufferedSocket
- Core: flush :: BufferedSocket -> IO ()
- Core: inBuffer :: BufferedSocket -> InputBuffer
- Core: isReadable :: BufferedSocket -> IO Bool
- Core: isWriteable :: BufferedSocket -> IO Bool
- Core: makeBufferedSocket :: (Socket, SockAddr) -> InputBufferSize -> OutputBufferSize -> IO BufferedSocket
- Core: nativeSocket :: BufferedSocket -> Socket
- Core: readByte :: BufferedSocket -> IO Word8
- Core: readLazy :: BufferedSocket -> Int -> IO ByteString
- Core: readRaw :: BufferedSocket -> Int -> IO ByteString
- Core: readToByte :: BufferedSocket -> Word8 -> IO ByteString
- Core: readToByteMax :: BufferedSocket -> Word8 -> MaxLength -> IO (Maybe ByteString)
- Core: readToByteStringMax :: BufferedSocket -> ByteString -> MaxLength -> IO (Maybe ByteString)
- Core: sendByteString :: BufferedSocket -> ByteString -> IO ()
- Core: type MaxLength = Int
- Core: type ReadSize = Int
- Core: waitForRead :: BufferedSocket -> Timeout -> IO Bool
- Reader: class Readable r
- Reader: class ReadableString s
- Reader: instance Readable Int16
- Reader: instance Readable Int32
- Reader: instance Readable Int64
- Reader: instance Readable Int8
- Reader: instance Readable Word16
- Reader: instance Readable Word32
- Reader: instance Readable Word64
- Reader: instance Readable Word8
- Reader: instance ReadableString ByteString
- Reader: read :: Readable r => BufferedSocket -> IO r
- Reader: readInt16 :: BufferedSocket -> IO Int16
- Reader: readInt32 :: BufferedSocket -> IO Int32
- Reader: readInt64 :: BufferedSocket -> IO Int64
- Reader: readInt8 :: BufferedSocket -> IO Int8
- Reader: readNativeString :: BufferedSocket -> ReadSize -> IO String
- Reader: readString :: ReadableString s => BufferedSocket -> Int -> IO s
- Reader: readText :: BufferedSocket -> ReadSize -> IO Text
- Reader: readTextLazy :: BufferedSocket -> ReadSize -> IO Text
- Reader: readWord16 :: BufferedSocket -> IO Word16
- Reader: readWord32 :: BufferedSocket -> IO Word32
- Reader: readWord64 :: BufferedSocket -> IO Word64
- Reader: readWord8 :: BufferedSocket -> IO Word8
- Writer: class Sendable s
- Writer: instance Sendable ByteString
- Writer: instance Sendable Int16
- Writer: instance Sendable Int32
- Writer: instance Sendable Int64
- Writer: instance Sendable Int8
- Writer: instance Sendable Word16
- Writer: instance Sendable Word32
- Writer: instance Sendable Word64
- Writer: instance Sendable Word8
- Writer: send :: Sendable s => BufferedSocket -> s -> IO ()
- Writer: sendInt16 :: BufferedSocket -> Int16 -> IO ()
- Writer: sendInt32 :: BufferedSocket -> Int32 -> IO ()
- Writer: sendInt64 :: BufferedSocket -> Int64 -> IO ()
- Writer: sendInt8 :: BufferedSocket -> Int8 -> IO ()
- Writer: sendString :: BufferedSocket -> String -> IO ()
- Writer: sendText :: BufferedSocket -> Text -> IO ()
- Writer: sendTextLazy :: BufferedSocket -> Text -> IO ()
- Writer: sendWord16 :: BufferedSocket -> Word16 -> IO ()
- Writer: sendWord32 :: BufferedSocket -> Word32 -> IO ()
- Writer: sendWord64 :: BufferedSocket -> Word64 -> IO ()
- Writer: sendWord8 :: BufferedSocket -> Word8 -> IO ()
+ Network.BufferedSocket.Core: closeRead :: BufferedSocket -> IO ()
+ Network.BufferedSocket.Core: closeWrite :: BufferedSocket -> IO ()
+ Network.BufferedSocket.Core: data BufferedSocket
+ Network.BufferedSocket.Core: flush :: BufferedSocket -> IO ()
+ Network.BufferedSocket.Core: inBuffer :: BufferedSocket -> InputBuffer
+ Network.BufferedSocket.Core: isReadable :: BufferedSocket -> IO Bool
+ Network.BufferedSocket.Core: isWriteable :: BufferedSocket -> IO Bool
+ Network.BufferedSocket.Core: makeBufferedSocket :: (Socket, SockAddr) -> InputBufferSize -> OutputBufferSize -> IO BufferedSocket
+ Network.BufferedSocket.Core: nativeSocket :: BufferedSocket -> Socket
+ Network.BufferedSocket.Core: readByte :: BufferedSocket -> IO Word8
+ Network.BufferedSocket.Core: readLazy :: BufferedSocket -> Int -> IO ByteString
+ Network.BufferedSocket.Core: readRaw :: BufferedSocket -> Int -> IO ByteString
+ Network.BufferedSocket.Core: readToByte :: BufferedSocket -> Word8 -> IO ByteString
+ Network.BufferedSocket.Core: readToByteMax :: BufferedSocket -> Word8 -> MaxLength -> IO (Maybe ByteString)
+ Network.BufferedSocket.Core: readToByteStringMax :: BufferedSocket -> ByteString -> MaxLength -> IO (Maybe ByteString)
+ Network.BufferedSocket.Core: sendByteString :: BufferedSocket -> ByteString -> IO ()
+ Network.BufferedSocket.Core: type MaxLength = Int
+ Network.BufferedSocket.Core: type ReadSize = Int
+ Network.BufferedSocket.Core: waitForRead :: BufferedSocket -> Timeout -> IO Bool
+ Network.BufferedSocket.Reader: class Readable r
+ Network.BufferedSocket.Reader: class ReadableString s
+ Network.BufferedSocket.Reader: instance Readable Int16
+ Network.BufferedSocket.Reader: instance Readable Int32
+ Network.BufferedSocket.Reader: instance Readable Int64
+ Network.BufferedSocket.Reader: instance Readable Int8
+ Network.BufferedSocket.Reader: instance Readable Word16
+ Network.BufferedSocket.Reader: instance Readable Word32
+ Network.BufferedSocket.Reader: instance Readable Word64
+ Network.BufferedSocket.Reader: instance Readable Word8
+ Network.BufferedSocket.Reader: instance ReadableString ByteString
+ Network.BufferedSocket.Reader: read :: Readable r => BufferedSocket -> IO r
+ Network.BufferedSocket.Reader: readInt16 :: BufferedSocket -> IO Int16
+ Network.BufferedSocket.Reader: readInt32 :: BufferedSocket -> IO Int32
+ Network.BufferedSocket.Reader: readInt64 :: BufferedSocket -> IO Int64
+ Network.BufferedSocket.Reader: readInt8 :: BufferedSocket -> IO Int8
+ Network.BufferedSocket.Reader: readNativeString :: BufferedSocket -> ReadSize -> IO String
+ Network.BufferedSocket.Reader: readString :: ReadableString s => BufferedSocket -> Int -> IO s
+ Network.BufferedSocket.Reader: readText :: BufferedSocket -> ReadSize -> IO Text
+ Network.BufferedSocket.Reader: readTextLazy :: BufferedSocket -> ReadSize -> IO Text
+ Network.BufferedSocket.Reader: readWord16 :: BufferedSocket -> IO Word16
+ Network.BufferedSocket.Reader: readWord32 :: BufferedSocket -> IO Word32
+ Network.BufferedSocket.Reader: readWord64 :: BufferedSocket -> IO Word64
+ Network.BufferedSocket.Reader: readWord8 :: BufferedSocket -> IO Word8
+ Network.BufferedSocket.Writer: class Sendable s
+ Network.BufferedSocket.Writer: instance Sendable ByteString
+ Network.BufferedSocket.Writer: instance Sendable Int16
+ Network.BufferedSocket.Writer: instance Sendable Int32
+ Network.BufferedSocket.Writer: instance Sendable Int64
+ Network.BufferedSocket.Writer: instance Sendable Int8
+ Network.BufferedSocket.Writer: instance Sendable Word16
+ Network.BufferedSocket.Writer: instance Sendable Word32
+ Network.BufferedSocket.Writer: instance Sendable Word64
+ Network.BufferedSocket.Writer: instance Sendable Word8
+ Network.BufferedSocket.Writer: send :: Sendable s => BufferedSocket -> s -> IO ()
+ Network.BufferedSocket.Writer: sendInt16 :: BufferedSocket -> Int16 -> IO ()
+ Network.BufferedSocket.Writer: sendInt32 :: BufferedSocket -> Int32 -> IO ()
+ Network.BufferedSocket.Writer: sendInt64 :: BufferedSocket -> Int64 -> IO ()
+ Network.BufferedSocket.Writer: sendInt8 :: BufferedSocket -> Int8 -> IO ()
+ Network.BufferedSocket.Writer: sendString :: BufferedSocket -> String -> IO ()
+ Network.BufferedSocket.Writer: sendText :: BufferedSocket -> Text -> IO ()
+ Network.BufferedSocket.Writer: sendTextLazy :: BufferedSocket -> Text -> IO ()
+ Network.BufferedSocket.Writer: sendWord16 :: BufferedSocket -> Word16 -> IO ()
+ Network.BufferedSocket.Writer: sendWord32 :: BufferedSocket -> Word32 -> IO ()
+ Network.BufferedSocket.Writer: sendWord64 :: BufferedSocket -> Word64 -> IO ()
+ Network.BufferedSocket.Writer: sendWord8 :: BufferedSocket -> Word8 -> IO ()
Files
- BufferedSocket.cabal +4/−4
- BufferedSocket.hs +0/−36
- Core.hs +0/−466
- Network/BufferedSocket.hs +36/−0
- Network/BufferedSocket/Core.hs +466/−0
- Network/BufferedSocket/Reader.hs +176/−0
- Network/BufferedSocket/Writer.hs +153/−0
- Reader.hs +0/−176
- Writer.hs +0/−153
BufferedSocket.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.1.1.0+version: 0.2.0.0 -- A short (one-line) description of the package. synopsis: A socker wrapper that makes the IO of sockets much cleaner@@ -48,10 +48,10 @@ library -- Modules exported by the library.- exposed-modules: BufferedSocket, Core, Reader, Writer- + exposed-modules: Network.BufferedSocket, Network.BufferedSocket.Core, Network.BufferedSocket.Reader, Network.BufferedSocket.Writer+ -- Modules included in this library but not exported.- -- other-modules: test+ -- other-modules: Network.BufferedSocket.test -- LANGUAGE extensions used by modules in this package. other-extensions: OverloadedStrings, FlexibleInstances
− BufferedSocket.hs
@@ -1,36 +0,0 @@-{------------------------------------------------------------------------------------------Module name: BufferedSocket-Made by: Tomas Möre 2015- --Usage:- This module is mean to be imported qualified -- ex: import Headers qualified BS --Notes: - Buffered sockets are a data type that is a kind of overlay on normal sockets. -- - All the exported read / write operations are build such that they ALLWAYS read / write the ammount of bytes requested - - - This package allows some cases of lazy IO. Some people see Lazy io as the devil incarné. However it is excpected that anyone using this module - is cabale of understanding any possible side effects.- --WARNINGS:- This module uses a ton of IO and non functional ways of solving problems. - This is because we want to be as spacetime efficient as possible. - - This module does NOT contain "beatiful" haskell code-------------------------------------------------------------------------------------------}---{-# LANGUAGE OverloadedStrings #-}--module BufferedSocket (module X) where--import Core as X-import Reader as X-import Writer as X
− Core.hs
@@ -1,466 +0,0 @@-{------------------------------------------------------------------------------------------Module name: BufferedSocket-Made by: Tomas Möre 2015- --Usage:- This module is mean to be imported qualified -- ex: import Headers qualified BS --Notes: - Buffered sockets are a data type that is a kind of overlay on normal sockets. -- - All the exported read / write operations are build such that they ALLWAYS read / write the ammount of bytes requested - - - This package allows some cases of lazy IO. Some people see Lazy io as the devil incarné. However it is excpected that anyone using this module - is cabale of understanding any possible side effects.- --WARNINGS:- This module uses a ton of IO and non functional ways of solving problems. - This is because we want to be as spacetime efficient as possible. - - This module does NOT contain "beatiful" haskell code-------------------------------------------------------------------------------------------}---{-# LANGUAGE OverloadedStrings #-}--module Core-(- readRaw-, readLazy-, readByte-, readToByte-, readToByteMax-, sendByteString-, readToByteStringMax-, flush-, BufferedSocket -, makeBufferedSocket-, MaxLength-, ReadSize--, inBuffer-, waitForRead-, isReadable-, isWriteable-, closeRead-, closeWrite-, nativeSocket- )-where --import Prelude hiding (getLine, read)----import Control.Monad-import Control.Applicative-import qualified Data.ByteString as B-import qualified Data.ByteString.Internal as BI-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Builder as BB -import qualified Data.ByteString.Char8 as BC8 -import qualified Data.Text as T -import qualified Data.Text.Lazy as TL --import Data.List-import Data.IORef-import Data.Functor-import Data.Maybe (isJust)--import qualified Network.Socket as NS -- ns for native socket -import Foreign.Storable-import Foreign.Ptr-import Foreign.C.Types-import System.IO.Unsafe -- I'm so sorry for this-import System.Posix.Types-import System.Timeout--import Foreign.ForeignPtr-import Data.Monoid-import Data.Word -import Data.String-import qualified Data.Text.Encoding as ENCSTRICT -import qualified Data.Text.Lazy.Encoding as ENCLAZY--import GHC.Conc.IO--type MaxLength = Int--type BufferSize = Int--type InputBufferSize = BufferSize-type OutputBufferSize = BufferSize--type BytesInBuffer = IORef Int -type ByteOffset = IORef Int --type RemainingBytes = Int --type ReadSize = Int --type Read = Int --type Timeout = Int --type SocketData = (NS.Socket,NS.SockAddr,Fd)--type ByteString = B.ByteString--- Since newtype is disregarded at compile time we make these newtypes to make sure no errors occur ---- The output buffer is simply a buffer that attemps to store a certain ammount of data before we send the package.--- This is to make minimalize the TCP overhead. Some operating systems might attemp to do the same. -type OutputBuffer = (ForeignPtr Word8, BytesInBuffer,BufferSize)-type InputBuffer = (ForeignPtr Word8, ByteOffset,BytesInBuffer,BufferSize)--newtype BufferedSocket = BufferedSocket (SocketData, InputBuffer, OutputBuffer)--{--UTIL --}---nativeSocket:: BufferedSocket -> NS.Socket-nativeSocket (BufferedSocket ((socket,_,_),_,_)) = socket --isReadable:: BufferedSocket -> IO Bool -isReadable (BufferedSocket ((socket,_,_),_,_)) = NS.sIsReadable socket--isWriteable:: BufferedSocket -> IO Bool -isWriteable (BufferedSocket ((socket,_,_),_,_)) = NS.isWritable socket--closeRead :: BufferedSocket -> IO ()-closeRead (BufferedSocket ((socket,_,_),_,_)) = NS.shutdown socket NS.ShutdownReceive--closeWrite :: BufferedSocket -> IO ()-closeWrite (BufferedSocket ((socket,_,_),_,_)) = NS.shutdown socket NS.ShutdownSend--- INPUT BUFFER -inBuffer :: BufferedSocket -> InputBuffer-inBuffer (BufferedSocket (_,inBuf,_)) = inBuf --inBufferClear :: InputBuffer -> IO () -inBufferClear (_, offsetRef, bytesRef, _) = writeIORef offsetRef 0 >> writeIORef bytesRef 0--inBufferReadAll :: InputBuffer -> IO ByteString-inBufferReadAll inBuf@(buf, offsetRef, bytesRef, _) =- do- offset <- readIORef offsetRef - bytesN <- readIORef bytesRef- let unreadBytes = bytesN - offset- if bytesN == 0- then return "" - else (inBufferClear inBuf) >> (return $ BI.fromForeignPtr buf offset unreadBytes)----- moves the bytes in the buffer to the start -inBufferRealign :: InputBuffer -> IO ()-inBufferRealign (buf, offsetRef, bytesRef, _) = - withForeignPtr buf $ \ptr -> - do putStrLn "realigned"- offset <- readIORef offsetRef- when (offset > 0) $- do bytesN <- readIORef bytesRef- let unreadBytes = bytesN - offset- offsetPtr = plusPtr ptr offset- BI.memcpy ptr offsetPtr unreadBytes- writeIORef offsetRef 0 --inBufferFindByteReal :: Ptr Word8 -> Int -> Word8 -> IO (Maybe Int)-inBufferFindByteReal _ 0 _ = return Nothing -inBufferFindByteReal ptr bytesLeft matchByte = - do currentChar <- peek ptr - if isMatch currentChar - then return $ Just bytesLeft- else inBufferFindByteReal (plusPtr ptr 1) (bytesLeft - 1) matchByte- where - isMatch = (==matchByte)---inBufferFindByte :: InputBuffer -> Word8 -> IO (Maybe Int)-inBufferFindByte (buf, offsetRef, bytesRef, _) byte =- do offset <- readIORef offsetRef - bytesN <- readIORef bytesRef - let unreadBytes = bytesN - offset-- withForeignPtr buf $ - \ ptr -> do let startPtr = plusPtr ptr offset- maybeBytesLeft <- inBufferFindByteReal startPtr unreadBytes byte- case maybeBytesLeft of - Just n -> return $ Just (unreadBytes - n) - Nothing -> return Nothing -- --- OUTPUT BUFFER -outBuffer :: BufferedSocket -> OutputBuffer -outBuffer (BufferedSocket (_,_,outBuf)) = outBuf----- SOCKET DATA -socketData :: BufferedSocket -> SocketData-socketData (BufferedSocket (sockData,_,_)) = sockData----- Reads up to max the available bytes of data-bSocketRecv :: BufferedSocket -> IO Int -bSocketRecv (BufferedSocket ((sock,_,_),(buf,offsetRef,bytesNRef,bufSize),_)) = - withForeignPtr buf $ \ptr -> - do bytesN <- readIORef bytesNRef - let offsetBuf = plusPtr ptr bytesN- maxRead = bufSize - bytesN- if maxRead > 0 - then - do - (recievedBytes, _) <- NS.recvBufFrom sock offsetBuf maxRead- writeIORef bytesNRef (bytesN + recievedBytes)- return recievedBytes- else - return 0 ---- Unsafe. Do not use if you don't know what you're doing -bSocketRecvMin :: BufferedSocket -> Int -> IO ()-bSocketRecvMin bSocket n = - do bytesRead <- bSocketRecv bSocket - when (bytesRead < n) $ bSocketRecvMin bSocket (n - bytesRead)--waitForRead :: BufferedSocket -> Timeout -> IO Bool -waitForRead (BufferedSocket ((sock,_,fideDesc),_,_)) timeoutDuration =- do maybeSucess <- timeout timeoutDuration (threadWaitRead fideDesc)- if isJust maybeSucess - then return True - else return False--{--CREATING A SOCKET--This puts together a new "BufferedSocket". or commonly refered as "bSock"-A buffered socket is a socket which is acommodated by a Ptr. Because of the need to split headers etc up at certain points but still-safet he rest of the data to be whatever the server requires it to be.--A buffered socket should be called with freeBSocket before disregarded. -The server thunk function does so automaticly.---}---BufferedSocket ((sock, sockAddr, socketFileDesc), inputBuffer, outPutBuffer)-makeBufferedSocket :: (NS.Socket, NS.SockAddr) -> InputBufferSize -> OutputBufferSize -> IO BufferedSocket-makeBufferedSocket (sock, sockAddr) inBufferSize outBufferSize = - do- inputBuffer <- makeInputBuffer inBufferSize- outPutBuffer <- makeOutputBuffer outBufferSize- let socketFileDesc = NS.fdSocket sock- return $ BufferedSocket ((sock, sockAddr, Fd socketFileDesc), inputBuffer, outPutBuffer)--makeInputBuffer :: InputBufferSize -> IO InputBuffer-makeInputBuffer bufferSize =- do offset <- newIORef 0 - bytesCount <- newIORef 0- buffer <- BI.mallocByteString bufferSize- return (buffer, offset, bytesCount, bufferSize)--makeOutputBuffer :: OutputBufferSize -> IO OutputBuffer-makeOutputBuffer bufferSize = - do bytesCount <- newIORef 0- buffer <- BI.mallocByteString bufferSize- return (buffer, bytesCount, bufferSize)--{--USING THE BUFFERED SOCKETS:--}--{- Strict reading -If the buffer contains the required ammount of bytes it will simply read it.-If not it will read all the bytes. Clear the buffer and attempt to read more ---} -readRaw :: BufferedSocket -> Int -> IO ByteString-readRaw _ 0 = return ""-readRaw bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) readSize = - do bytesN <- readIORef bytesNRef- offset <- readIORef offsetRef-- let unreadBytes = bytesN - offset- availableBytesAfterBytesN = bufSize - bytesN- availableBytesTotal = availableBytesAfterBytesN + offset-- missingBytes = readSize - unreadBytes-- if missingBytes <= 0 - then writeIORef offsetRef (offset + readSize) >> (return $ BI.fromForeignPtr buf offset readSize)- else if missingBytes <= availableBytesAfterBytesN - then bSocketRecvMin bSock missingBytes >> - loop- else if missingBytes <= availableBytesTotal- then inBufferRealign inBuf >> - fillBuffer availableBytesTotal >>- loop- else do - let strFragment = if unreadBytes > 0 - then BI.fromForeignPtr buf offset unreadBytes- else ""- inBufferClear inBuf - (strFragment<>) <$> readRaw bSock (readSize - B.length strFragment)- where - fillBuffer = bSocketRecvMin bSock- loop = readRaw bSock readSize---- -lazyReader :: BufferedSocket -> [Int] -> IO [ByteString]-lazyReader _ [] = return []-lazyReader bSock ~(chunkSize:rest) = - do chunk <- unsafeInterleaveIO $ readRaw bSock chunkSize- next <- unsafeInterleaveIO $ lazyReader bSock rest - return (chunk:next)---- readLazy will read will read in chunks of the same size of the buffered socket -readLazy :: BufferedSocket -> Int -> IO BL.ByteString-readLazy _ 0 = return ""-readLazy bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) readSize = - let chunkSizes = unfoldr (\b -> if b == 0 - then Nothing - else if b > bufSize - then Just (bufSize, b - bufSize) - else Just (b,0)) readSize- in BL.fromChunks <$> lazyReader bSock chunkSizes---readByte :: BufferedSocket -> IO Word8-readByte bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) =- do bytesN <- readIORef bytesNRef - offset <- readIORef offsetRef- let unreadBytes = bytesN - offset-- if unreadBytes > 0 - then do - writeIORef offsetRef (offset + 1)- withForeignPtr buf (`peekByteOff` offset)- else do - inBufferClear inBuf - bSocketRecvMin bSock 1 - readByte bSock--readToByte :: BufferedSocket -> Word8 -> IO ByteString-readToByte bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) byte = - do maybeByteIndex <- inBufferFindByte inBuf byte- case maybeByteIndex of - Nothing -> (<>) <$> inBufferReadAll inBuf <*> readToByte bSock byte -- >>= (<>) fmap (<>(readToByte bSock byte)) $ inBufferReadAll inBuf- Just n -> do - offset <- readIORef offsetRef - writeIORef offsetRef (offset + n + 1)- return $ BI.fromForeignPtr buf offset n--readToByteMax :: BufferedSocket -> Word8 -> MaxLength -> IO (Maybe ByteString)-readToByteMax bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) byte maxLen - | hasNoMaxLen = return Nothing - | otherwise = - do maybeByteIndex <- inBufferFindByte inBuf byte- bytesN <- readIORef bytesNRef- offset <- readIORef offsetRef -- let unreadBytes = bytesN - offset-- case maybeByteIndex of- Nothing -> if unreadBytes >= maxLen- then return Nothing - else do let maxBytesLeft = maxLen - unreadBytes- thisBufData <- inBufferReadAll inBuf -- empties the current buffer data- bSocketRecv bSock -- attempts to rebuffer the socket - maybeData <- readToByteMax bSock byte maxBytesLeft -- repeat - case maybeData of - Nothing -> return Nothing - Just a -> return $ Just (thisBufData <> a)- Just n -> do writeIORef offsetRef (offset + n + 1)- return $ Just $ BI.fromForeignPtr buf offset n - where - hasNoMaxLen = maxLen <= 0---- Dangerous will read forever if nothing is found --- You will most probably want to use the readToByteStringMax-readToByteString :: BufferedSocket -> ByteString -> IO ByteString-readToByteString _ "" = error "readToByteString can not take an empty bytestring" -readToByteString bSock searchString =- do dataString <- readToByte bSock firstByte- trail <- readRaw bSock $ B.length restSearchString- if trail == restSearchString- then return dataString- else ((dataString<>trail)<>) <$> readToByteString bSock searchString- where - firstByte = B.head searchString - restSearchString = B.tail searchString---- Same as above but Will read to a max length. If this is reached It returns "Nothing"-readToByteStringMax :: BufferedSocket -> ByteString -> MaxLength -> IO (Maybe ByteString)-readToByteStringMax _ _ 0 = return Nothing-readToByteStringMax _ "" _ = error "readToByteString max got an empty string"-readToByteStringMax bSock searchString maxLength = - do maybeDataString <- readToByteMax bSock firstByte maxLength- case maybeDataString of - Nothing -> return Nothing - Just dataString -> do trail <- readRaw bSock $ B.length restSearchString- if trail == restSearchString- then return $ Just dataString- else do maybeMoreString <- readToByteStringMax bSock searchString (maxLength - B.length dataString - B.length trail)- case maybeMoreString of - Nothing -> return Nothing - Just a -> return $ Just $ dataString <> trail <> a- where - firstByte = B.head searchString - restSearchString = B.tail searchString----- Simple and clean. force pushed the data to the actual socket. Afther this -flush :: BufferedSocket -> IO()-flush (BufferedSocket ((socket,sockAddr,_),_,(outBuf, bytesNRef, bufferSize))) =- do bytesInBuffer <- readIORef bytesNRef- when (bytesInBuffer > 0) $- do let sender ptr bytes = - do sent <- NS.sendBufTo socket ptr bytes sockAddr- when (sent < bytes) $ sender (plusPtr ptr sent) (bytes - sent)-- str = BI.fromForeignPtr outBuf 0 bytesInBuffer- withForeignPtr outBuf (`sender` bytesInBuffer)- writeIORef bytesNRef 0- ---- if the string sent to this function is less or equal to the ammount of bytes left in the buffer --- we will simply fill the buffer --- (if equal) we will also flush this buffer.--- If the string is greater then the ammount of bytes left --- we will attempt to fill the last bytes in the buffer then -sendByteString:: BufferedSocket -> ByteString -> IO ()-sendByteString _ "" = return ()-sendByteString bSock@(BufferedSocket ((sock,sockAddr,_),_,(outBuf, bytesNRef, bufferSize))) outputStr = - do bytesInBuffer <- readIORef bytesNRef- let bytesLeftInBuffer = bufferSize - bytesInBuffer-- if srcLength <= bytesLeftInBuffer- then do- withForeignPtr outBuf $ \bufPtr -> - withForeignPtr sourceFrgnPtr $ \srcPtr -> - BI.memcpy (plusPtr bufPtr bytesInBuffer) (plusPtr srcPtr srcOffet) srcLength- - writeIORef bytesNRef (bytesInBuffer + srcLength)- when (srcLength + bytesInBuffer == bufferSize) $ flush bSock- else do -- let (current,rest) = B.splitAt bytesLeftInBuffer outputStr- overflowRest = div (B.length rest) bufferSize- directSendChunk = B.take overflowRest rest - directSendLen = B.length directSendChunk- (directSend,_,_)= BI.toForeignPtr directSendChunk-- toBufferPart = B.drop overflowRest rest-- sendByteString bSock current - when (directSendChunk /= "") $ withForeignPtr sourceFrgnPtr $ \ptr -> void $ NS.sendBufTo sock (plusPtr ptr srcOffet) directSendLen sockAddr- sendByteString bSock toBufferPart-- where - (sourceFrgnPtr, srcOffet, srcLength) = BI.toForeignPtr outputStr-- --
+ Network/BufferedSocket.hs view
@@ -0,0 +1,36 @@+{-----------------------------------------------------------------------------------------+Module name: BufferedSocket+Made by: Tomas Möre 2015+ ++Usage:+ This module is mean to be imported qualified ++ ex: import Headers qualified BS ++Notes: + Buffered sockets are a data type that is a kind of overlay on normal sockets. ++ + All the exported read / write operations are build such that they ALLWAYS read / write the ammount of bytes requested + + + This package allows some cases of lazy IO. Some people see Lazy io as the devil incarné. However it is excpected that anyone using this module + is cabale of understanding any possible side effects.+ ++WARNINGS:+ This module uses a ton of IO and non functional ways of solving problems. + This is because we want to be as spacetime efficient as possible. + + This module does NOT contain "beatiful" haskell code+------------------------------------------------------------------------------------------}+++{-# LANGUAGE OverloadedStrings #-}++module Network.BufferedSocket (module Network.BufferedSocket.Core , module Network.BufferedSocket.Reader, module Network.BufferedSocket.Writer) where++import Network.BufferedSocket.Core +import Network.BufferedSocket.Reader+import Network.BufferedSocket.Writer
+ Network/BufferedSocket/Core.hs view
@@ -0,0 +1,466 @@+{-----------------------------------------------------------------------------------------+Module name: BufferedSocket+Made by: Tomas Möre 2015+ ++Usage:+ This module is mean to be imported qualified ++ ex: import Headers qualified BS ++Notes: + Buffered sockets are a data type that is a kind of overlay on normal sockets. ++ + All the exported read / write operations are build such that they ALLWAYS read / write the ammount of bytes requested + + + This package allows some cases of lazy IO. Some people see Lazy io as the devil incarné. However it is excpected that anyone using this module + is cabale of understanding any possible side effects.+ ++WARNINGS:+ This module uses a ton of IO and non functional ways of solving problems. + This is because we want to be as spacetime efficient as possible. + + This module does NOT contain "beatiful" haskell code+------------------------------------------------------------------------------------------}+++{-# LANGUAGE OverloadedStrings #-}++module Network.BufferedSocket.Core+(+ readRaw+, readLazy+, readByte+, readToByte+, readToByteMax+, sendByteString+, readToByteStringMax+, flush+, BufferedSocket +, makeBufferedSocket+, MaxLength+, ReadSize++, inBuffer+, waitForRead+, isReadable+, isWriteable+, closeRead+, closeWrite+, nativeSocket+ )+where ++import Prelude hiding (getLine, read)++++import Control.Monad+import Control.Applicative+import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as BI+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Builder as BB +import qualified Data.ByteString.Char8 as BC8 +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL ++import Data.List+import Data.IORef+import Data.Functor+import Data.Maybe (isJust)++import qualified Network.Socket as NS -- ns for native socket +import Foreign.Storable+import Foreign.Ptr+import Foreign.C.Types+import System.IO.Unsafe -- I'm so sorry for this+import System.Posix.Types+import System.Timeout++import Foreign.ForeignPtr+import Data.Monoid+import Data.Word +import Data.String+import qualified Data.Text.Encoding as ENCSTRICT +import qualified Data.Text.Lazy.Encoding as ENCLAZY++import GHC.Conc.IO++type MaxLength = Int++type BufferSize = Int++type InputBufferSize = BufferSize+type OutputBufferSize = BufferSize++type BytesInBuffer = IORef Int +type ByteOffset = IORef Int ++type RemainingBytes = Int ++type ReadSize = Int ++type Read = Int ++type Timeout = Int ++type SocketData = (NS.Socket,NS.SockAddr,Fd)++type ByteString = B.ByteString+-- Since newtype is disregarded at compile time we make these newtypes to make sure no errors occur ++-- The output buffer is simply a buffer that attemps to store a certain ammount of data before we send the package.+-- This is to make minimalize the TCP overhead. Some operating systems might attemp to do the same. +type OutputBuffer = (ForeignPtr Word8, BytesInBuffer,BufferSize)+type InputBuffer = (ForeignPtr Word8, ByteOffset,BytesInBuffer,BufferSize)++newtype BufferedSocket = BufferedSocket (SocketData, InputBuffer, OutputBuffer)++{-+UTIL +-}+++nativeSocket:: BufferedSocket -> NS.Socket+nativeSocket (BufferedSocket ((socket,_,_),_,_)) = socket ++isReadable:: BufferedSocket -> IO Bool +isReadable (BufferedSocket ((socket,_,_),_,_)) = NS.sIsReadable socket++isWriteable:: BufferedSocket -> IO Bool +isWriteable (BufferedSocket ((socket,_,_),_,_)) = NS.isWritable socket++closeRead :: BufferedSocket -> IO ()+closeRead (BufferedSocket ((socket,_,_),_,_)) = NS.shutdown socket NS.ShutdownReceive++closeWrite :: BufferedSocket -> IO ()+closeWrite (BufferedSocket ((socket,_,_),_,_)) = NS.shutdown socket NS.ShutdownSend+-- INPUT BUFFER +inBuffer :: BufferedSocket -> InputBuffer+inBuffer (BufferedSocket (_,inBuf,_)) = inBuf ++inBufferClear :: InputBuffer -> IO () +inBufferClear (_, offsetRef, bytesRef, _) = writeIORef offsetRef 0 >> writeIORef bytesRef 0++inBufferReadAll :: InputBuffer -> IO ByteString+inBufferReadAll inBuf@(buf, offsetRef, bytesRef, _) =+ do+ offset <- readIORef offsetRef + bytesN <- readIORef bytesRef+ let unreadBytes = bytesN - offset+ if bytesN == 0+ then return "" + else (inBufferClear inBuf) >> (return $ BI.fromForeignPtr buf offset unreadBytes)+++-- moves the bytes in the buffer to the start +inBufferRealign :: InputBuffer -> IO ()+inBufferRealign (buf, offsetRef, bytesRef, _) = + withForeignPtr buf $ \ptr -> + do putStrLn "realigned"+ offset <- readIORef offsetRef+ when (offset > 0) $+ do bytesN <- readIORef bytesRef+ let unreadBytes = bytesN - offset+ offsetPtr = plusPtr ptr offset+ BI.memcpy ptr offsetPtr unreadBytes+ writeIORef offsetRef 0 ++inBufferFindByteReal :: Ptr Word8 -> Int -> Word8 -> IO (Maybe Int)+inBufferFindByteReal _ 0 _ = return Nothing +inBufferFindByteReal ptr bytesLeft matchByte = + do currentChar <- peek ptr + if isMatch currentChar + then return $ Just bytesLeft+ else inBufferFindByteReal (plusPtr ptr 1) (bytesLeft - 1) matchByte+ where + isMatch = (==matchByte)+++inBufferFindByte :: InputBuffer -> Word8 -> IO (Maybe Int)+inBufferFindByte (buf, offsetRef, bytesRef, _) byte =+ do offset <- readIORef offsetRef + bytesN <- readIORef bytesRef + let unreadBytes = bytesN - offset++ withForeignPtr buf $ + \ ptr -> do let startPtr = plusPtr ptr offset+ maybeBytesLeft <- inBufferFindByteReal startPtr unreadBytes byte+ case maybeBytesLeft of + Just n -> return $ Just (unreadBytes - n) + Nothing -> return Nothing ++ +-- OUTPUT BUFFER +outBuffer :: BufferedSocket -> OutputBuffer +outBuffer (BufferedSocket (_,_,outBuf)) = outBuf+++-- SOCKET DATA +socketData :: BufferedSocket -> SocketData+socketData (BufferedSocket (sockData,_,_)) = sockData+++-- Reads up to max the available bytes of data+bSocketRecv :: BufferedSocket -> IO Int +bSocketRecv (BufferedSocket ((sock,_,_),(buf,offsetRef,bytesNRef,bufSize),_)) = + withForeignPtr buf $ \ptr -> + do bytesN <- readIORef bytesNRef + let offsetBuf = plusPtr ptr bytesN+ maxRead = bufSize - bytesN+ if maxRead > 0 + then + do + (recievedBytes, _) <- NS.recvBufFrom sock offsetBuf maxRead+ writeIORef bytesNRef (bytesN + recievedBytes)+ return recievedBytes+ else + return 0 ++-- Unsafe. Do not use if you don't know what you're doing +bSocketRecvMin :: BufferedSocket -> Int -> IO ()+bSocketRecvMin bSocket n = + do bytesRead <- bSocketRecv bSocket + when (bytesRead < n) $ bSocketRecvMin bSocket (n - bytesRead)++waitForRead :: BufferedSocket -> Timeout -> IO Bool +waitForRead (BufferedSocket ((sock,_,fideDesc),_,_)) timeoutDuration =+ do maybeSucess <- timeout timeoutDuration (threadWaitRead fideDesc)+ if isJust maybeSucess + then return True + else return False++{-+CREATING A SOCKET++This puts together a new "BufferedSocket". or commonly refered as "bSock"+A buffered socket is a socket which is acommodated by a Ptr. Because of the need to split headers etc up at certain points but still+safet he rest of the data to be whatever the server requires it to be.++A buffered socket should be called with freeBSocket before disregarded. +The server thunk function does so automaticly.++-}+--BufferedSocket ((sock, sockAddr, socketFileDesc), inputBuffer, outPutBuffer)+makeBufferedSocket :: (NS.Socket, NS.SockAddr) -> InputBufferSize -> OutputBufferSize -> IO BufferedSocket+makeBufferedSocket (sock, sockAddr) inBufferSize outBufferSize = + do+ inputBuffer <- makeInputBuffer inBufferSize+ outPutBuffer <- makeOutputBuffer outBufferSize+ let socketFileDesc = NS.fdSocket sock+ return $ BufferedSocket ((sock, sockAddr, Fd socketFileDesc), inputBuffer, outPutBuffer)++makeInputBuffer :: InputBufferSize -> IO InputBuffer+makeInputBuffer bufferSize =+ do offset <- newIORef 0 + bytesCount <- newIORef 0+ buffer <- BI.mallocByteString bufferSize+ return (buffer, offset, bytesCount, bufferSize)++makeOutputBuffer :: OutputBufferSize -> IO OutputBuffer+makeOutputBuffer bufferSize = + do bytesCount <- newIORef 0+ buffer <- BI.mallocByteString bufferSize+ return (buffer, bytesCount, bufferSize)++{-+USING THE BUFFERED SOCKETS:+-}++{- Strict reading +If the buffer contains the required ammount of bytes it will simply read it.+If not it will read all the bytes. Clear the buffer and attempt to read more ++-} +readRaw :: BufferedSocket -> Int -> IO ByteString+readRaw _ 0 = return ""+readRaw bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) readSize = + do bytesN <- readIORef bytesNRef+ offset <- readIORef offsetRef++ let unreadBytes = bytesN - offset+ availableBytesAfterBytesN = bufSize - bytesN+ availableBytesTotal = availableBytesAfterBytesN + offset++ missingBytes = readSize - unreadBytes++ if missingBytes <= 0 + then writeIORef offsetRef (offset + readSize) >> (return $ BI.fromForeignPtr buf offset readSize)+ else if missingBytes <= availableBytesAfterBytesN + then bSocketRecvMin bSock missingBytes >> + loop+ else if missingBytes <= availableBytesTotal+ then inBufferRealign inBuf >> + fillBuffer availableBytesTotal >>+ loop+ else do + let strFragment = if unreadBytes > 0 + then BI.fromForeignPtr buf offset unreadBytes+ else ""+ inBufferClear inBuf + (strFragment<>) <$> readRaw bSock (readSize - B.length strFragment)+ where + fillBuffer = bSocketRecvMin bSock+ loop = readRaw bSock readSize++-- +lazyReader :: BufferedSocket -> [Int] -> IO [ByteString]+lazyReader _ [] = return []+lazyReader bSock ~(chunkSize:rest) = + do chunk <- unsafeInterleaveIO $ readRaw bSock chunkSize+ next <- unsafeInterleaveIO $ lazyReader bSock rest + return (chunk:next)++-- readLazy will read will read in chunks of the same size of the buffered socket +readLazy :: BufferedSocket -> Int -> IO BL.ByteString+readLazy _ 0 = return ""+readLazy bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) readSize = + let chunkSizes = unfoldr (\b -> if b == 0 + then Nothing + else if b > bufSize + then Just (bufSize, b - bufSize) + else Just (b,0)) readSize+ in BL.fromChunks <$> lazyReader bSock chunkSizes+++readByte :: BufferedSocket -> IO Word8+readByte bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) =+ do bytesN <- readIORef bytesNRef + offset <- readIORef offsetRef+ let unreadBytes = bytesN - offset++ if unreadBytes > 0 + then do + writeIORef offsetRef (offset + 1)+ withForeignPtr buf (`peekByteOff` offset)+ else do + inBufferClear inBuf + bSocketRecvMin bSock 1 + readByte bSock++readToByte :: BufferedSocket -> Word8 -> IO ByteString+readToByte bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) byte = + do maybeByteIndex <- inBufferFindByte inBuf byte+ case maybeByteIndex of + Nothing -> (<>) <$> inBufferReadAll inBuf <*> readToByte bSock byte -- >>= (<>) fmap (<>(readToByte bSock byte)) $ inBufferReadAll inBuf+ Just n -> do + offset <- readIORef offsetRef + writeIORef offsetRef (offset + n + 1)+ return $ BI.fromForeignPtr buf offset n++readToByteMax :: BufferedSocket -> Word8 -> MaxLength -> IO (Maybe ByteString)+readToByteMax bSock@(BufferedSocket ((sock,_,_),inBuf@(buf,offsetRef,bytesNRef,bufSize),_)) byte maxLen + | hasNoMaxLen = return Nothing + | otherwise = + do maybeByteIndex <- inBufferFindByte inBuf byte+ bytesN <- readIORef bytesNRef+ offset <- readIORef offsetRef ++ let unreadBytes = bytesN - offset++ case maybeByteIndex of+ Nothing -> if unreadBytes >= maxLen+ then return Nothing + else do let maxBytesLeft = maxLen - unreadBytes+ thisBufData <- inBufferReadAll inBuf -- empties the current buffer data+ bSocketRecv bSock -- attempts to rebuffer the socket + maybeData <- readToByteMax bSock byte maxBytesLeft -- repeat + case maybeData of + Nothing -> return Nothing + Just a -> return $ Just (thisBufData <> a)+ Just n -> do writeIORef offsetRef (offset + n + 1)+ return $ Just $ BI.fromForeignPtr buf offset n + where + hasNoMaxLen = maxLen <= 0++-- Dangerous will read forever if nothing is found +-- You will most probably want to use the readToByteStringMax+readToByteString :: BufferedSocket -> ByteString -> IO ByteString+readToByteString _ "" = error "readToByteString can not take an empty bytestring" +readToByteString bSock searchString =+ do dataString <- readToByte bSock firstByte+ trail <- readRaw bSock $ B.length restSearchString+ if trail == restSearchString+ then return dataString+ else ((dataString<>trail)<>) <$> readToByteString bSock searchString+ where + firstByte = B.head searchString + restSearchString = B.tail searchString++-- Same as above but Will read to a max length. If this is reached It returns "Nothing"+readToByteStringMax :: BufferedSocket -> ByteString -> MaxLength -> IO (Maybe ByteString)+readToByteStringMax _ _ 0 = return Nothing+readToByteStringMax _ "" _ = error "readToByteString max got an empty string"+readToByteStringMax bSock searchString maxLength = + do maybeDataString <- readToByteMax bSock firstByte maxLength+ case maybeDataString of + Nothing -> return Nothing + Just dataString -> do trail <- readRaw bSock $ B.length restSearchString+ if trail == restSearchString+ then return $ Just dataString+ else do maybeMoreString <- readToByteStringMax bSock searchString (maxLength - B.length dataString - B.length trail)+ case maybeMoreString of + Nothing -> return Nothing + Just a -> return $ Just $ dataString <> trail <> a+ where + firstByte = B.head searchString + restSearchString = B.tail searchString+++-- Simple and clean. force pushed the data to the actual socket. Afther this +flush :: BufferedSocket -> IO()+flush (BufferedSocket ((socket,sockAddr,_),_,(outBuf, bytesNRef, bufferSize))) =+ do bytesInBuffer <- readIORef bytesNRef+ when (bytesInBuffer > 0) $+ do let sender ptr bytes = + do sent <- NS.sendBufTo socket ptr bytes sockAddr+ when (sent < bytes) $ sender (plusPtr ptr sent) (bytes - sent)++ str = BI.fromForeignPtr outBuf 0 bytesInBuffer+ withForeignPtr outBuf (`sender` bytesInBuffer)+ writeIORef bytesNRef 0+ ++-- if the string sent to this function is less or equal to the ammount of bytes left in the buffer +-- we will simply fill the buffer +-- (if equal) we will also flush this buffer.+-- If the string is greater then the ammount of bytes left +-- we will attempt to fill the last bytes in the buffer then +sendByteString:: BufferedSocket -> ByteString -> IO ()+sendByteString _ "" = return ()+sendByteString bSock@(BufferedSocket ((sock,sockAddr,_),_,(outBuf, bytesNRef, bufferSize))) outputStr = + do bytesInBuffer <- readIORef bytesNRef+ let bytesLeftInBuffer = bufferSize - bytesInBuffer++ if srcLength <= bytesLeftInBuffer+ then do+ withForeignPtr outBuf $ \bufPtr -> + withForeignPtr sourceFrgnPtr $ \srcPtr -> + BI.memcpy (plusPtr bufPtr bytesInBuffer) (plusPtr srcPtr srcOffet) srcLength+ + writeIORef bytesNRef (bytesInBuffer + srcLength)+ when (srcLength + bytesInBuffer == bufferSize) $ flush bSock+ else do ++ let (current,rest) = B.splitAt bytesLeftInBuffer outputStr+ overflowRest = div (B.length rest) bufferSize+ directSendChunk = B.take overflowRest rest + directSendLen = B.length directSendChunk+ (directSend,_,_)= BI.toForeignPtr directSendChunk++ toBufferPart = B.drop overflowRest rest++ sendByteString bSock current + when (directSendChunk /= "") $ withForeignPtr sourceFrgnPtr $ \ptr -> void $ NS.sendBufTo sock (plusPtr ptr srcOffet) directSendLen sockAddr+ sendByteString bSock toBufferPart++ where + (sourceFrgnPtr, srcOffet, srcLength) = BI.toForeignPtr outputStr++ ++
+ Network/BufferedSocket/Reader.hs view
@@ -0,0 +1,176 @@+{-----------------------------------------------------------------------------------------+Module name: BufferedSocketReader+Made by: Tomas Möre 2015+ ++Usage:+ This module is mean to be imported qualified + This is an addition to BufferedSocket+ ex: import Headers qualified BS++Notes: + This reader modules purpose is to make it easto to read diffrent kinds of data types. This is of course only if the socket isn't encrypted.++WARNINGS:+ Nah Nothing +------------------------------------------------------------------------------------------}+++{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}++module Network.BufferedSocket.Reader+( readText+, readTextLazy+, readNativeString+, readWord8+, readWord16+, readWord32+, readWord64+, readInt8+, readInt16+, readInt32+, readInt64+, read+, Readable+, readString+, ReadableString+ )+where ++import Prelude hiding (getLine, read)+++++import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as BI+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Builder as BB +import qualified Data.ByteString.Char8 as BC8 +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL ++import qualified Data.Text.Encoding as ENC +import qualified Data.Text.Lazy.Encoding as ENCL++import qualified Data.Text.Encoding.Error as ENCERROR+import Data.Maybe (isJust)++import Control.Monad++import Data.Functor ++import Data.Monoid+import Data.Word +import Data.String+import Data.Int ++import qualified Network.BufferedSocket.Core as BS ++import Data.Bits ++listToValue :: (Bits a, Num a) => [a] -> a +listToValue = foldl xor zeroBits+++convertToShiftedList:: (Bits b, Num b) => [Word8] -> Int -> [b]+convertToShiftedList [] _ = []+convertToShiftedList (x:xs) byteIndex = + let n = byteIndex - 1 -- Since we want the size of the result data the actual index -||- is (-1)+ in (shift (fromIntegral x) (8 * n): convertToShiftedList xs n) +++readText:: BS.BufferedSocket -> BS.ReadSize -> IO T.Text+readText bSocket readSize = do+ byteData <- BS.readRaw bSocket readSize+ return $ ENC.decodeUtf8With (ENCERROR.replace '\xfffd') byteData++readTextLazy:: BS.BufferedSocket -> BS.ReadSize -> IO TL.Text+readTextLazy bSocket readSize = do+ byteData <- BS.readLazy bSocket readSize+ return $ ENCL.decodeUtf8With (ENCERROR.replace '\xfffd') byteData++readNativeString:: BS.BufferedSocket -> BS.ReadSize -> IO String +readNativeString bSock readSize = do + byteString <- readText bSock readSize+ return $ T.unpack byteString ++readWord8:: BS.BufferedSocket -> IO Word8+readWord8 = BS.readByte++readWord16:: BS.BufferedSocket -> IO Word16+readWord16 bSocket = do + bytes <- B.unpack <$> BS.readRaw bSocket byteSize+ let result = convertToShiftedList bytes byteSize:: [Word16]+ return $ listToValue result++ where + byteSize = 2++readWord32:: BS.BufferedSocket -> IO Word32+readWord32 bSocket = do + bytes <- B.unpack <$> BS.readRaw bSocket byteSize+ let result = convertToShiftedList bytes byteSize:: [Word32]+ return $ listToValue result++ where + byteSize = 4++readWord64:: BS.BufferedSocket -> IO Word64+readWord64 bSocket = do + bytes <- B.unpack <$> BS.readRaw bSocket byteSize+ let result = convertToShiftedList bytes byteSize:: [Word64]+ return $ listToValue result++ where + byteSize = 8+readInt8:: BS.BufferedSocket -> IO Int8+readInt8 = (fromIntegral <$>) . readWord8++readInt16:: BS.BufferedSocket -> IO Int16+readInt16 = (fromIntegral <$>) . readWord16++readInt32:: BS.BufferedSocket -> IO Int32+readInt32 = (fromIntegral <$>) . readWord32++readInt64:: BS.BufferedSocket -> IO Int64+readInt64 = (fromIntegral <$>) . readWord64+++class Readable r where + read :: (BS.BufferedSocket -> IO r)++instance Readable Word8 where + read = readWord8+instance Readable Word16 where + read = readWord16+instance Readable Word32 where + read = readWord32+instance Readable Word64 where + read = readWord64++instance Readable Int8 where + read = readInt8+instance Readable Int16 where + read = readInt16+instance Readable Int32 where + read = readInt32+instance Readable Int64 where + read = readInt64++class ReadableString s where + readString :: (BS.BufferedSocket -> Int -> IO s)++instance ReadableString B.ByteString where + readString = BS.readRaw+instance ReadableString BL.ByteString where + readString = BS.readLazy+{-- +instance ReadableString T.Text where + readString = readText+instance ReadableString TL.Text where + readString = readTextLazy+instance ReadableString String where + readString = readNativeString+--}
+ Network/BufferedSocket/Writer.hs view
@@ -0,0 +1,153 @@+{-----------------------------------------------------------------------------------------+Module name: BufferedSocketReader+Made by: Tomas Möre 2015+ ++Usage:+ This module is mean to be imported qualified + This is an addition to BufferedSocket+ ex: import Headers qualified BS++Notes: + This reader modules purpose is to make it easto to read diffrent kinds of data types. This is of course only if the socket isn't encrypted.++WARNINGS:+------------------------------------------------------------------------------------------}+++{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+module Network.BufferedSocket.Writer+( sendText+, sendTextLazy+, sendString+, sendWord8+, sendWord16+, sendWord32+, sendWord64+, sendInt8+, sendInt16+, sendInt32+, sendInt64+, Sendable+, send + )+where ++import Prelude hiding (getLine, read)++++import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as BI+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Builder as BB +import qualified Data.ByteString.Char8 as BC8 +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL ++import qualified Data.Text.Encoding as ENC +import qualified Data.Text.Lazy.Encoding as ENCL+++import Control.Monad+import Data.Functor+import Data.Monoid+import Data.Word +import Data.String+import Data.Int +import Data.Bits++import Foreign.Storable++import qualified Network.BufferedSocket.Core as BS ++bitsToByteStringReal:: (Bits a, Integral a) => a -> Int -> [Word8]+bitsToByteStringReal a 0 = [fromIntegral a]+bitsToByteStringReal inData size = + let thisByte = shift inData (- (size * 8 ))+ in (fromIntegral thisByte : bitsToByteStringReal inData (size - 1))++bitsToByteString:: (Bits a, Integral a, Storable a) => a -> B.ByteString+bitsToByteString inData = B.pack $ bitsToByteStringReal inData $ sizeOf inData - 1+++sendWord8:: BS.BufferedSocket -> Word8 -> IO ()+sendWord8 bSocket byte = BS.sendByteString bSocket $ B.pack [byte] ++sendByte :: BS.BufferedSocket -> Word8 -> IO ()+sendByte = sendWord8++sendWord16:: BS.BufferedSocket -> Word16 -> IO ()+sendWord16 bSocket word16 = let sendData = bitsToByteString word16+ in do + putStrLn $ "sent bytes: " ++ (show $ B.unpack sendData)+ BS.sendByteString bSocket $ sendData+++sendWord32:: BS.BufferedSocket -> Word32 -> IO ()+sendWord32 bSocket word32 = BS.sendByteString bSocket $ bitsToByteString word32++sendWord64:: BS.BufferedSocket -> Word64 -> IO ()+sendWord64 bSocket word64 = BS.sendByteString bSocket $ bitsToByteString word64+sendInt8:: BS.BufferedSocket -> Int8 -> IO ()+sendInt8 bSocket int8 = sendWord8 bSocket $ fromIntegral int8+sendInt16:: BS.BufferedSocket -> Int16 -> IO ()+sendInt16 bSocket int16 = sendWord16 bSocket $ (fromIntegral int16 :: Word16)+sendInt32:: BS.BufferedSocket -> Int32 -> IO ()+sendInt32 bSocket int32 = sendWord32 bSocket $ fromIntegral int32+sendInt64:: BS.BufferedSocket -> Int64 -> IO ()+sendInt64 bSocket int64 = sendWord64 bSocket $ fromIntegral int64++sendLazyReal :: BS.BufferedSocket -> [B.ByteString] -> IO () +sendLazyReal _ [] = return ()+sendLazyReal bSocket (x:xs) = BS.sendByteString bSocket x >> sendLazyReal bSocket xs++sendLazy :: BS.BufferedSocket -> BL.ByteString -> IO ()+sendLazy bSocket lazyBytestring = sendLazyReal bSocket $ BL.toChunks lazyBytestring+++sendText:: BS.BufferedSocket -> T.Text -> IO ()+sendText bSocket textData = BS.sendByteString bSocket $ ENC.encodeUtf8 textData++sendTextLazy:: BS.BufferedSocket -> TL.Text -> IO ()+sendTextLazy bSocket lazyText = sendLazy bSocket $ ENCL.encodeUtf8 lazyText++sendString:: BS.BufferedSocket -> String -> IO ()+sendString bSocket string = sendLazy bSocket $ fromString string++++class Sendable s where+ send :: (BS.BufferedSocket -> s -> IO ()) ++instance Sendable Word8 where + send = sendWord8+instance Sendable Word16 where + send = sendWord16+instance Sendable Word32 where + send = sendWord32+instance Sendable Word64 where + send = sendWord64++instance Sendable Int8 where + send = sendInt8+instance Sendable Int16 where + send = sendInt16+instance Sendable Int32 where + send = sendInt32+instance Sendable Int64 where + send = sendInt64 ++instance Sendable B.ByteString where + send = BS.sendByteString+instance Sendable BL.ByteString where + send = sendLazy+{-- +instance Sendable T.Text where + send = sendText+instance Sendable TL.Text where + send = sendTextLazy+instance Sendable String where + send = sendString+--}
− Reader.hs
@@ -1,176 +0,0 @@-{------------------------------------------------------------------------------------------Module name: BufferedSocketReader-Made by: Tomas Möre 2015- --Usage:- This module is mean to be imported qualified - This is an addition to BufferedSocket- ex: import Headers qualified BS--Notes: - This reader modules purpose is to make it easto to read diffrent kinds of data types. This is of course only if the socket isn't encrypted.--WARNINGS:- Nah Nothing -------------------------------------------------------------------------------------------}---{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleInstances #-}--module Reader-( readText-, readTextLazy-, readNativeString-, readWord8-, readWord16-, readWord32-, readWord64-, readInt8-, readInt16-, readInt32-, readInt64-, read-, Readable-, readString-, ReadableString- )-where --import Prelude hiding (getLine, read)-----import qualified Data.ByteString as B-import qualified Data.ByteString.Internal as BI-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Builder as BB -import qualified Data.ByteString.Char8 as BC8 -import qualified Data.Text as T -import qualified Data.Text.Lazy as TL --import qualified Data.Text.Encoding as ENC -import qualified Data.Text.Lazy.Encoding as ENCL--import qualified Data.Text.Encoding.Error as ENCERROR-import Data.Maybe (isJust)--import Control.Monad--import Data.Functor --import Data.Monoid-import Data.Word -import Data.String-import Data.Int --import qualified Core as BS --import Data.Bits --listToValue :: (Bits a, Num a) => [a] -> a -listToValue = foldl xor zeroBits---convertToShiftedList:: (Bits b, Num b) => [Word8] -> Int -> [b]-convertToShiftedList [] _ = []-convertToShiftedList (x:xs) byteIndex = - let n = byteIndex - 1 -- Since we want the size of the result data the actual index -||- is (-1)- in (shift (fromIntegral x) (8 * n): convertToShiftedList xs n) ---readText:: BS.BufferedSocket -> BS.ReadSize -> IO T.Text-readText bSocket readSize = do- byteData <- BS.readRaw bSocket readSize- return $ ENC.decodeUtf8With (ENCERROR.replace '\xfffd') byteData--readTextLazy:: BS.BufferedSocket -> BS.ReadSize -> IO TL.Text-readTextLazy bSocket readSize = do- byteData <- BS.readLazy bSocket readSize- return $ ENCL.decodeUtf8With (ENCERROR.replace '\xfffd') byteData--readNativeString:: BS.BufferedSocket -> BS.ReadSize -> IO String -readNativeString bSock readSize = do - byteString <- readText bSock readSize- return $ T.unpack byteString --readWord8:: BS.BufferedSocket -> IO Word8-readWord8 = BS.readByte--readWord16:: BS.BufferedSocket -> IO Word16-readWord16 bSocket = do - bytes <- B.unpack <$> BS.readRaw bSocket byteSize- let result = convertToShiftedList bytes byteSize:: [Word16]- return $ listToValue result-- where - byteSize = 2--readWord32:: BS.BufferedSocket -> IO Word32-readWord32 bSocket = do - bytes <- B.unpack <$> BS.readRaw bSocket byteSize- let result = convertToShiftedList bytes byteSize:: [Word32]- return $ listToValue result-- where - byteSize = 4--readWord64:: BS.BufferedSocket -> IO Word64-readWord64 bSocket = do - bytes <- B.unpack <$> BS.readRaw bSocket byteSize- let result = convertToShiftedList bytes byteSize:: [Word64]- return $ listToValue result-- where - byteSize = 8-readInt8:: BS.BufferedSocket -> IO Int8-readInt8 = (fromIntegral <$>) . readWord8--readInt16:: BS.BufferedSocket -> IO Int16-readInt16 = (fromIntegral <$>) . readWord16--readInt32:: BS.BufferedSocket -> IO Int32-readInt32 = (fromIntegral <$>) . readWord32--readInt64:: BS.BufferedSocket -> IO Int64-readInt64 = (fromIntegral <$>) . readWord64---class Readable r where - read :: (BS.BufferedSocket -> IO r)--instance Readable Word8 where - read = readWord8-instance Readable Word16 where - read = readWord16-instance Readable Word32 where - read = readWord32-instance Readable Word64 where - read = readWord64--instance Readable Int8 where - read = readInt8-instance Readable Int16 where - read = readInt16-instance Readable Int32 where - read = readInt32-instance Readable Int64 where - read = readInt64--class ReadableString s where - readString :: (BS.BufferedSocket -> Int -> IO s)--instance ReadableString B.ByteString where - readString = BS.readRaw-instance ReadableString BL.ByteString where - readString = BS.readLazy-{-- -instance ReadableString T.Text where - readString = readText-instance ReadableString TL.Text where - readString = readTextLazy-instance ReadableString String where - readString = readNativeString---}
− Writer.hs
@@ -1,153 +0,0 @@-{------------------------------------------------------------------------------------------Module name: BufferedSocketReader-Made by: Tomas Möre 2015- --Usage:- This module is mean to be imported qualified - This is an addition to BufferedSocket- ex: import Headers qualified BS--Notes: - This reader modules purpose is to make it easto to read diffrent kinds of data types. This is of course only if the socket isn't encrypted.--WARNINGS:-------------------------------------------------------------------------------------------}---{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleInstances #-}-module Writer-( sendText-, sendTextLazy-, sendString-, sendWord8-, sendWord16-, sendWord32-, sendWord64-, sendInt8-, sendInt16-, sendInt32-, sendInt64-, Sendable-, send - )-where --import Prelude hiding (getLine, read)----import qualified Data.ByteString as B-import qualified Data.ByteString.Internal as BI-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Builder as BB -import qualified Data.ByteString.Char8 as BC8 -import qualified Data.Text as T -import qualified Data.Text.Lazy as TL --import qualified Data.Text.Encoding as ENC -import qualified Data.Text.Lazy.Encoding as ENCL---import Control.Monad-import Data.Functor-import Data.Monoid-import Data.Word -import Data.String-import Data.Int -import Data.Bits--import Foreign.Storable--import qualified Core as BS --bitsToByteStringReal:: (Bits a, Integral a) => a -> Int -> [Word8]-bitsToByteStringReal a 0 = [fromIntegral a]-bitsToByteStringReal inData size = - let thisByte = shift inData (- (size * 8 ))- in (fromIntegral thisByte : bitsToByteStringReal inData (size - 1))--bitsToByteString:: (Bits a, Integral a, Storable a) => a -> B.ByteString-bitsToByteString inData = B.pack $ bitsToByteStringReal inData $ sizeOf inData - 1---sendWord8:: BS.BufferedSocket -> Word8 -> IO ()-sendWord8 bSocket byte = BS.sendByteString bSocket $ B.pack [byte] --sendByte :: BS.BufferedSocket -> Word8 -> IO ()-sendByte = sendWord8--sendWord16:: BS.BufferedSocket -> Word16 -> IO ()-sendWord16 bSocket word16 = let sendData = bitsToByteString word16- in do - putStrLn $ "sent bytes: " ++ (show $ B.unpack sendData)- BS.sendByteString bSocket $ sendData---sendWord32:: BS.BufferedSocket -> Word32 -> IO ()-sendWord32 bSocket word32 = BS.sendByteString bSocket $ bitsToByteString word32--sendWord64:: BS.BufferedSocket -> Word64 -> IO ()-sendWord64 bSocket word64 = BS.sendByteString bSocket $ bitsToByteString word64-sendInt8:: BS.BufferedSocket -> Int8 -> IO ()-sendInt8 bSocket int8 = sendWord8 bSocket $ fromIntegral int8-sendInt16:: BS.BufferedSocket -> Int16 -> IO ()-sendInt16 bSocket int16 = sendWord16 bSocket $ (fromIntegral int16 :: Word16)-sendInt32:: BS.BufferedSocket -> Int32 -> IO ()-sendInt32 bSocket int32 = sendWord32 bSocket $ fromIntegral int32-sendInt64:: BS.BufferedSocket -> Int64 -> IO ()-sendInt64 bSocket int64 = sendWord64 bSocket $ fromIntegral int64--sendLazyReal :: BS.BufferedSocket -> [B.ByteString] -> IO () -sendLazyReal _ [] = return ()-sendLazyReal bSocket (x:xs) = BS.sendByteString bSocket x >> sendLazyReal bSocket xs--sendLazy :: BS.BufferedSocket -> BL.ByteString -> IO ()-sendLazy bSocket lazyBytestring = sendLazyReal bSocket $ BL.toChunks lazyBytestring---sendText:: BS.BufferedSocket -> T.Text -> IO ()-sendText bSocket textData = BS.sendByteString bSocket $ ENC.encodeUtf8 textData--sendTextLazy:: BS.BufferedSocket -> TL.Text -> IO ()-sendTextLazy bSocket lazyText = sendLazy bSocket $ ENCL.encodeUtf8 lazyText--sendString:: BS.BufferedSocket -> String -> IO ()-sendString bSocket string = sendLazy bSocket $ fromString string----class Sendable s where- send :: (BS.BufferedSocket -> s -> IO ()) --instance Sendable Word8 where - send = sendWord8-instance Sendable Word16 where - send = sendWord16-instance Sendable Word32 where - send = sendWord32-instance Sendable Word64 where - send = sendWord64--instance Sendable Int8 where - send = sendInt8-instance Sendable Int16 where - send = sendInt16-instance Sendable Int32 where - send = sendInt32-instance Sendable Int64 where - send = sendInt64 --instance Sendable B.ByteString where - send = BS.sendByteString-instance Sendable BL.ByteString where - send = sendLazy-{-- -instance Sendable T.Text where - send = sendText-instance Sendable TL.Text where - send = sendTextLazy-instance Sendable String where - send = sendString---}