NineP 0.0.0 → 0.0.1
raw patch · 2 files changed
+79/−5 lines, 2 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.NineP: getBytes32 :: Get ByteString
- Data.NineP: getList16 :: (Bin a) => Get [a]
- Data.NineP: getListAll :: (Bin a) => Get [a]
- Data.NineP: getNest :: (Integral n) => n -> Get a -> Get a
- Data.NineP: getNestList16 :: (Bin a) => Get [a]
- Data.NineP: getTag :: VarMsg -> Tag
- Data.NineP: getVarMsg :: Tag -> Get VarMsg
- Data.NineP: maxSize :: Word32
- Data.NineP: putBytes32 :: ByteString -> Put
- Data.NineP: putList16 :: (Bin a) => [a] -> Put
- Data.NineP: putListAll :: (Bin a) => [a] -> Put
- Data.NineP: putNestList16 :: (Bin a) => [a] -> Put
- Data.NineP: putVarMsg :: VarMsg -> Put
Files
- NineP.cabal +1/−1
- src/Data/NineP.hs +78/−4
NineP.cabal view
@@ -1,5 +1,5 @@ name: NineP-version: 0.0.0+version: 0.0.1 license: BSD3 license-file: LICENSE author: Tim Newsham <newsham@lava.net>, Dave Leimbach <leimy2k@gmail.com>
src/Data/NineP.hs view
@@ -1,5 +1,55 @@ {-# LANGUAGE TypeSynonymInstances #-}-module Data.NineP where+-----------------------------------------------------------------------------+-- |+-- Module : Data.NineP+-- Copyright : Tim Newsham+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : David Leimbach <leimy2k@gmail.com>+-- Stability : experimental+-- Portability : Only tested on GHC 6.12.1, uses TypeSynonnymInstances+--+-- Binary serialization of 9P messages to and from lazy ByteStrings.+-- This library does not currently provide any networking support or +-- wrappers for easy to write clients or servers, though that may come +-- with time as we decide the best way to implement these.+-- +-- Perhaps interesting about 9P is that the messages are send in little endian+-- as opposed to the somewhat misnamed "network byte order" that most people+-- refer to for "big endian".+--+-- It's fairly easy to use runPut or runGet to generate or parse 9P messages+-- example: +--+-- send sock $ runPut (put (Msg TTVersion (-1) & Tversion 1024 "9P2000"))+-- +-- This sends a lazy ByteString over a socket to begin a handshake of a 9P +-- connection with a server, negotiating the use of the "9P2000" protocol, +-- with a maximum message size of 1KB, using a tag of (-1) (known as NOTAG in +-- 9P space.+--+-- This 9P implementation has been lightly tested against an Inferno operating+-- system share with no authentication successfully.+-----------------------------------------------------------------------------++module Data.NineP ( + -- * Bin is the little endian encode/decode class for 9P2000+ Bin(..)+ -- * The Qid class (http://9p.cat-v.org for details)+ , Qid(..)++ -- * like stat for unix filesystems, but for 9P2000+ , Stat(..)++ -- * The Msg type is an enveleope for 9P2000 messages+ , Msg(..)+ + -- ** Tag is the (possibly misnamed) message payload type+ , Tag(..)++ -- ** VarMsg is an algebraic type allowing for the various types of messages 9P2000 can have to be grouped under one type+ , VarMsg(..)+ ) where import Control.Applicative import Control.Monad import Data.Binary.Get@@ -21,31 +71,39 @@ instance Bin Word8 where get = getWord8 put = putWord8+ instance Bin Word16 where get = getWord16le put = putWord16le+ instance Bin Word32 where get = getWord32le put = putWord32le+ instance Bin Word64 where get = getWord64le put = putWord64le+ instance Bin Char where get = chr . fromIntegral <$> getWord8 put = putWord8 . fromIntegral . ord+ instance Bin String where get = getWord16le >>= \n -> replicateM (fromIntegral n) get put xs = putWord16le (fromIntegral $ length xs) >> mapM_ put xs +-- | A Plan 9 Qid type. See http://9p.cat-v.org for more information data Qid = Qid { qid_typ :: Word8, qid_vers :: Word32, qid_path :: Word64 } deriving (Show, Eq) +-- Instnace of Bin for Qid. instance Bin Qid where get = Qid <$> get <*> get <*> get put (Qid t v p) = put t >> put v >> put p +-- | Takes a fixed size lazy ByteString @sz@, running @g@ on it, and returning the result if all the input is consumed, uses @Prelude.error@ otherwise. (Throws an exception) getNest :: Integral n => n -> Get a -> Get a getNest sz g = do b <- getLazyByteString (fromIntegral sz)@@ -58,7 +116,7 @@ n <- remaining error $ show n ++ " extra bytes in nested structure" -+-- | Provides information on a path entry at a 9P2000 server data Stat = Stat { st_typ :: Word16, st_dev :: Word32,@@ -83,7 +141,7 @@ putLazyByteString buf where p = put a >> put b >> put c >> put d >> put e >> put f >> put g >> put h >> put i >> put j >> put k -+-- | A variable message type that encapsulates the valid kinds of messages in a 9P2000 payload data VarMsg = Tversion { tv_msize :: Word32,@@ -147,6 +205,7 @@ deriving (Show, Eq) +-- | A type that enumerates all the valid (and one invalid) message types in 9P2000 data Tag = TTversion | TRversion | TTauth | TRauth | TTattach | TRattach | XXX_TTerror | TRerror | TTflush | TRflush | TTwalk | TRwalk | TTopen | TRopen @@ -163,32 +222,44 @@ else error $ "invalid tag: " ++ (show n) put = putWord8 . toEnum . (+ 100) . fromEnum +-- | A monadic action in Get that returns all parseable entries as a list, returning [] if none exist getListAll :: (Bin a) => Get [a] getListAll = do e <- isEmpty if e then return [] else (:) <$> get <*> getListAll++-- | A monadic action in Put that maps all encodable items into the Put monad for serialization putListAll :: (Bin a) => [a] -> Put putListAll = mapM_ put +-- | Like 'getNest' but is preceded by a little endian 16bit word. Useful for retrieving 16bit payload lengths from 9P2000 messages that support it. getNestList16 :: (Bin a) => Get [a] getNestList16 = do n <- getWord16le getNest n getListAll++-- | Runs 'putListAll' over @xs@ followed by computing the 16bit (little endian) length of the list, and prepending it to the final payload. Useful for automatically computing lengths for 9P2000 messages that require it putNestList16 :: Bin a => [a] -> Put putNestList16 xs = do let buf = runPut (putListAll xs) putWord16le $ fromIntegral $ L.length buf putLazyByteString buf +-- | Gets a 16bit value from the stream, then executes @Data.Binary.Get.get@ that many times to produce a list of parsed values. getList16 :: Bin a => Get [a] getList16 = getWord16le >>= \n -> replicateM (fromIntegral n) get++-- | Puts @xs@ into the Put stream prepended by its length. putList16 :: Bin a => [a] -> Put putList16 xs = putWord16le (fromIntegral $ length xs) >> mapM_ put xs +-- | Gets a 32bit little endian legnth from the stream, then gets a lazy ByteString of that size from the stream, returning only the ByteString getBytes32 :: Get L.ByteString getBytes32 = getWord32le >>= getLazyByteString . fromIntegral++-- | Takes length of @xs@ and then prepends that to the lazy ByteString it places in the Stream putBytes32 :: L.ByteString -> Put putBytes32 xs = putWord32le (fromIntegral $ L.length xs) >> putLazyByteString xs @@ -221,6 +292,7 @@ getTag (Twstat _ _) = TTwstat getTag (Rwstat) = TRwstat +-- | For every messages type, runs a Get parser to decode that type of payload from the 9P2000 stream getVarMsg :: Tag -> Get VarMsg getVarMsg TTversion = Tversion <$> get <*> get getVarMsg TRversion = Rversion <$> get <*> get@@ -251,6 +323,7 @@ getVarMsg TTwstat = Twstat <$> get <*> getNestList16 getVarMsg TRwstat = return Rwstat +-- | For every lower level VarMsg type, encodes a full wrapper around that type for use with 9P2000 streams putVarMsg :: VarMsg -> Put putVarMsg (Tversion a b) = put a >> put b putVarMsg (Rversion a b) = put a >> put b@@ -280,12 +353,13 @@ putVarMsg (Twstat a b) = put a >> putNestList16 b putVarMsg (Rwstat) = return () -+-- | The message envelope type for all 9P2000 messages data Msg = Msg { msg_typ :: Tag, msg_tag :: Word16, msg_body :: VarMsg } deriving(Show, Eq) +-- | An arbitrarily chosen maxiumum size for any Msg maxSize :: Word32 maxSize = 1024 * 1024 -- XXX arbitrary, configured?