on 63407dc help sign in
157 lines 5.7 kB history raw
1module Streamly.Internal.Data.Serialize
2 ( Size(..)
3 , Serialize(..)
4 ) where
6--------------------------------------------------------------------------------
7-- Imports
8--------------------------------------------------------------------------------
10import Control.Monad (void)
11import Data.List (foldl')
12import Data.Proxy (Proxy (..))
13import Streamly.Internal.Data.Unbox (MutableByteArray(..))
15import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))
16import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))
17import GHC.Stable (StablePtr(..))
19import qualified Streamly.Internal.Data.Unbox as Unbox
21import GHC.Exts
23--------------------------------------------------------------------------------
24-- Types
25--------------------------------------------------------------------------------
27-- | Info about the length of a serializable type. Length can depend on the
28-- value or can be independent.
29data Size a
30 = VarSize (a -> Int)
31 | ConstSize !Int
33-- | A type implementing the 'Serialize' interface supplies operations for
34-- reading and writing the type from and to a mutable byte array (an unboxed
35-- representation of the type) in memory. The read operation 'deserialize'
36-- deserializes the boxed type from the mutable byte array. The write operation
37-- 'serialize' serializes the boxed type to the mutable byte array.
38--
39-- IMPORTANT: The serialized data's byte ordering is not normalized, which means
40-- it remains the same as the machine's byte order where the function is
41-- executed. Consequently, it may not be compatible across machines with
42-- different byte ordering.
43--
44-- 'Serialize' contains enough information to serialize and deserialize variable
45-- length types.
46--
47-- >>> import Streamly.Internal.Data.Serialize (Serialize(..), Size(..))
48--
49-- >>> :{
50-- data Object = Object
51-- { _varLen :: [Int]
52-- , _constLen :: Int
53-- }
54-- :}
55--
56-- >>> :{
57-- instance Serialize Object where
58-- size =
59-- case (size :: Size [Int], size :: Size Int) of
60-- (VarSize f, ConstSize g) ->
61-- VarSize $ \obj ->
62-- f (_varLen obj) + g
63-- _ -> error "size is not defined properly"
64-- deserialize i arr = do
65-- (i1, x0) <- deserialize i arr
66-- (i2, x1) <- deserialize i1 arr
67-- pure (i2, Object x0 x1)
68-- serialize i arr (Object x0 x1) = do
69-- i1 <- serialize i arr x0
70-- i2 <- serialize i1 arr x1
71-- pure i2
72-- :}
73--
74class Serialize a where
75 -- | Get the 'Size', in bytes, reqired to store the serialized
76 -- representation of the type. Size cannot be zero.
77 size :: Size a
79 -- We can implement the following functions without returning the `Int`
80 -- offset but that may require traversing the Haskell structure again to get
81 -- the size. Therefore, this is a performance optimization.
83 -- | Deserialize a value from the given byte-index in the array. Returns a
84 -- tuple of the next byte-index and the deserialized value.
85 deserialize :: Int -> MutableByteArray -> IO (Int, a)
87 -- | Write the serialized representation of the value in the array at the
88 -- given byte-index. Returns the next byte-index.
89 serialize :: Int -> MutableByteArray -> a -> IO Int
91--------------------------------------------------------------------------------
92-- Instances
93--------------------------------------------------------------------------------
95#define DERIVE_SERIALIZE_FROM_UNBOX(_type) \
96instance Serialize _type where \
97; size = ConstSize $ Unbox.sizeOf (Proxy :: Proxy _type) \
98; deserialize off arr = \
99 Unbox.peekByteIndex off arr >>= \
100 \val -> let sz = Unbox.sizeOf (Proxy :: Proxy _type) \
101 in pure (off + sz, val) \
102; serialize off arr val = \
103 Unbox.pokeByteIndex off arr val \
104 >> let sz = Unbox.sizeOf (Proxy :: Proxy _type) \
105 in pure (off + sz)
107DERIVE_SERIALIZE_FROM_UNBOX(Char)
108DERIVE_SERIALIZE_FROM_UNBOX(Int8)
109DERIVE_SERIALIZE_FROM_UNBOX(Int16)
110DERIVE_SERIALIZE_FROM_UNBOX(Int32)
111DERIVE_SERIALIZE_FROM_UNBOX(Int)
112DERIVE_SERIALIZE_FROM_UNBOX(Int64)
113DERIVE_SERIALIZE_FROM_UNBOX(Word)
114DERIVE_SERIALIZE_FROM_UNBOX(Word8)
115DERIVE_SERIALIZE_FROM_UNBOX(Word16)
116DERIVE_SERIALIZE_FROM_UNBOX(Word32)
117DERIVE_SERIALIZE_FROM_UNBOX(Word64)
118DERIVE_SERIALIZE_FROM_UNBOX(Double)
119DERIVE_SERIALIZE_FROM_UNBOX(Float)
120DERIVE_SERIALIZE_FROM_UNBOX((StablePtr a))
121DERIVE_SERIALIZE_FROM_UNBOX((Ptr a))
122DERIVE_SERIALIZE_FROM_UNBOX((FunPtr a))
124instance forall a. Serialize a => Serialize [a] where
126 {-# INLINE size #-}
127 size = VarSize $ \lst ->
128 case size :: Size a of
129 VarSize f ->
130 foldl'
131 (\acc x -> acc + f x)
132 (Unbox.sizeOf (Proxy :: Proxy Int))
133 lst
134 ConstSize sz ->
135 length lst
136 * sz
137 + Unbox.sizeOf (Proxy :: Proxy Int)
139 {-# INLINE deserialize #-}
140 deserialize off arr = do
141 len <- Unbox.peekByteIndex off arr :: IO Int
142 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Int)
143 let peekList buf o 0 = pure (o, reverse buf)
144 peekList buf o i = do
145 (o1, x) <- deserialize o arr
146 peekList (x:buf) o1 (i - 1)
147 peekList [] off1 len
149 {-# INLINE serialize #-}
150 serialize off arr val = do
151 void $ serialize off arr (length val)
152 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Int)
153 let pokeList o [] = pure o
154 pokeList o (x:xs) = do
155 o1 <- serialize o arr x
156 pokeList o1 xs
157 pokeList off1 val