% Copyright (C) 2009 John Millikin % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see . \ignore{ \begin{code} module DBus.Marshal (marshal) where import Control.Arrow (first) import Control.Monad (msum) import qualified Control.Monad.State as S import Data.Maybe (fromJust) import Data.Word (Word8, Word16, Word32, Word64) import Data.Int (Int16, Int32, Int64) import qualified Data.ByteString.Lazy as L import Data.ByteString.Lazy.UTF8 (fromString) import qualified Data.Binary.Put as P import qualified Data.Binary.IEEE754 as IEEE import DBus.Padding (padding, alignment) import qualified DBus.Types as T \end{code} } \clearpage \section{Marshaling} \subsection{\tt marshal} \begin{code} marshal :: T.Endianness -> [T.Variant] -> L.ByteString marshal e vs = runMarshal (mapM_ marshalAny vs) e \end{code} \begin{code} marshalAny :: T.Variant -> Marshal marshalAny x = marshal' (T.variantType x) x where v :: T.Variable a => T.Variant -> a v = fromJust . T.fromVariant marshal' T.BooleanT = bool . v marshal' T.ByteT = word8 . v marshal' T.UInt16T = word16 . v marshal' T.UInt32T = word32 . v marshal' T.UInt64T = word64 . v marshal' T.Int16T = int16 . v marshal' T.Int32T = int32 . v marshal' T.Int64T = int64 . v marshal' T.DoubleT = double . v marshal' T.StringT = string . v marshal' T.ObjectPathT = objectPath . v marshal' T.SignatureT = signature . v marshal' (T.ArrayT _) = array . v marshal' (T.DictionaryT _ _) = dictionary . v marshal' (T.StructureT _) = structure . v marshal' T.VariantT = variant . v \end{code} \subsection{Atoms} \begin{code} bool :: Bool -> Marshal bool x = word32 (if x then 1 else 0) \end{code} \begin{code} word8 :: Word8 -> Marshal word8 x = append (L.pack [x]) \end{code} \begin{code} word16 :: Word16 -> Marshal word16 = appendPut P.putWord16be \end{code} \begin{code} word32 :: Word32 -> Marshal word32 = appendPut P.putWord32be \end{code} \begin{code} word64 :: Word64 -> Marshal word64 = appendPut P.putWord64be \end{code} \begin{code} int16 :: Int16 -> Marshal int16 = appendPut P.putWord16be . fromIntegral \end{code} \begin{code} int32 :: Int32 -> Marshal int32 = appendPut P.putWord32be . fromIntegral \end{code} \begin{code} int64 :: Int64 -> Marshal int64 = appendPut P.putWord64be . fromIntegral \end{code} \begin{code} double :: Double -> Marshal double = appendPut IEEE.putFloat64be \end{code} \begin{code} string :: String -> Marshal string x = do let bytes = fromString x word32 . fromIntegral . L.length $ bytes append bytes append (L.pack [0]) \end{code} \begin{code} objectPath :: T.ObjectPath -> Marshal objectPath = string . T.strObjectPath \end{code} \begin{code} signature :: T.Signature -> Marshal signature x = do let bytes = fromString . T.strSignature $ x word8 . fromIntegral . L.length $ bytes append bytes append (L.pack [0]) \end{code} \subsection{Containers} \subsubsection{Arrays} Marshaling arrays is complicated, because the array body must be marshaled \emph{first} to calculate the array length. This requires building a temporary marshaler, to get the padding right. \begin{code} array :: T.Array -> Marshal array x = do (arrayPadding, arrayBytes) <- getArrayBytes x word32 . fromIntegral . L.length $ arrayBytes append arrayPadding append arrayBytes \end{code} \begin{code} getArrayBytes :: T.Array -> MarshalM (L.ByteString, L.ByteString) getArrayBytes x = do let vs = T.arrayItems x let [T.ArrayT itemType] = T.signatureTypes . T.arraySignature $ x s <- S.get (MarshalState _ afterLength) <- word32 0 >> S.get (MarshalState _ afterPadding) <- pad (alignment itemType) >> S.get (MarshalState _ afterItems) <- mapM_ marshalAny vs >> S.get let paddingBytes = L.drop (L.length afterLength) afterPadding let itemBytes = L.drop (L.length afterPadding) afterItems S.put s return (paddingBytes, itemBytes) \end{code} \subsubsection{Dictionaries} \begin{code} dictionary :: T.Dictionary -> Marshal dictionary x = array x' where pairs = map (first T.atomToVariant) (T.dictionaryItems x) structs = [T.Structure [k,v] | (k,v) <- pairs] x' = fromJust . T.toArray $ structs \end{code} \subsubsection{Structures} \begin{code} structure :: T.Structure -> Marshal structure (T.Structure xs) = pad 8 >> mapM_ marshalAny xs \end{code} \subsubsection{Variants} \begin{code} variant :: T.Variant -> Marshal variant x = signature (T.variantSignature x) >> marshalAny x \end{code} \subsection{The {\tt Marshal} monad} {\tt Marshal} implements stateful marshaling, which is required for padding to be calculated properly. \begin{code} data MarshalState = MarshalState T.Endianness L.ByteString type MarshalM = S.State MarshalState type Marshal = MarshalM () \end{code} \begin{code} runMarshal :: Marshal -> T.Endianness -> L.ByteString runMarshal m e = bytes where initialState = MarshalState e L.empty (MarshalState _ bytes) = S.execState m initialState \end{code} \begin{code} append :: L.ByteString -> Marshal append bs = do (MarshalState e bs') <- S.get S.put $ MarshalState e (L.append bs' bs) \end{code} Add padding to the end of the marshaled bytes, until the length is a multiple of {\tt count}. \begin{code} pad :: Word8 -> Marshal pad count = do (MarshalState _ bytes) <- S.get let padding' = padding (fromIntegral . L.length $ bytes) count append $ L.replicate (fromIntegral padding') 0 \end{code} \begin{code} appendPut :: (a -> P.Put) -> a -> Marshal appendPut put x = do let bytes = P.runPut $ put x (MarshalState e _) <- S.get pad . fromIntegral . L.length $ bytes append $ case e of T.BigEndian -> bytes T.LittleEndian -> L.reverse bytes \end{code}