2-- Module : Streamly.Internal.Data.Serialize
3-- Copyright : (c) 2023 Composewell Technologies
4-- License : BSD3-3-Clause
5-- Maintainer : streamly@composewell.com
9module Streamly.Internal.Data.Serialize
17--------------------------------------------------------------------------------
19--------------------------------------------------------------------------------
23import Control.Monad (void)
24import Data.List (foldl')
25import Data.Proxy (Proxy (..))
26import Streamly.Internal.Data.Unbox (MutableByteArray(..), PinnedState(..))
27import Streamly.Internal.Data.Array.Type (Array(..))
28import Streamly.Internal.System.IO (unsafeInlineIO)
29import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))
30import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))
31import GHC.Stable (StablePtr(..))
33import qualified Streamly.Internal.Data.Unbox as Unbox
34import qualified Streamly.Internal.Data.Array as Array
38--------------------------------------------------------------------------------
40--------------------------------------------------------------------------------
42-- | Info about the length of a serializable type. Length can depend on the
43-- value or can be independent.
48-- | A type implementing the 'Serialize' interface supplies operations for
49-- reading and writing the type from and to a mutable byte array (an unboxed
50-- representation of the type) in memory. The read operation 'deserialize'
51-- deserializes the boxed type from the mutable byte array. The write operation
52-- 'serialize' serializes the boxed type to the mutable byte array.
54-- IMPORTANT: The serialized data's byte ordering is not normalized, which means
55-- it remains the same as the machine's byte order where the function is
56-- executed. Consequently, it may not be compatible across machines with
57-- different byte ordering.
59-- 'Serialize' contains enough information to serialize and deserialize variable
62-- >>> import Streamly.Internal.Data.Serialize (Serialize(..), Size(..))
65-- data Object = Object
72-- instance Serialize Object where
74-- case (size :: Size [Int], size :: Size Int) of
75-- (VarSize f, ConstSize g) ->
78-- _ -> error "size is not defined properly"
79-- deserialize i arr = do
80-- (i1, x0) <- deserialize i arr
81-- (i2, x1) <- deserialize i1 arr
82-- pure (i2, Object x0 x1)
83-- serialize i arr (Object x0 x1) = do
84-- i1 <- serialize i arr x0
85-- i2 <- serialize i1 arr x1
89class Serialize a where
90 -- | Get the 'Size', in bytes, reqired to store the serialized
91 -- representation of the type. Size cannot be zero.
94 -- We can implement the following functions without returning the `Int`
95 -- offset but that may require traversing the Haskell structure again to get
96 -- the size. Therefore, this is a performance optimization.
98 -- | Deserialize a value from the given byte-index in the array. Returns a
99 -- tuple of the next byte-index and the deserialized value.
100 deserialize :: Int -> MutableByteArray -> IO (Int, a)
102 -- | Write the serialized representation of the value in the array at the
103 -- given byte-index. Returns the next byte-index.
104 serialize :: Int -> MutableByteArray -> a -> IO Int
106--------------------------------------------------------------------------------
108--------------------------------------------------------------------------------
110#define DERIVE_SERIALIZE_FROM_UNBOX(_type) \
111instance Serialize _type where \
112; {-# INLINE size #-} \
113; size = ConstSize $ Unbox.sizeOf (Proxy :: Proxy _type) \
114; {-# INLINE deserialize #-} \
115; deserialize off arr = \
116 Unbox.peekByteIndex off arr >>= \
117 \val -> let sz = Unbox.sizeOf (Proxy :: Proxy _type) \
118 in pure (off + sz, val) \
119; {-# INLINE serialize #-} \
120; serialize off arr val = \
121 Unbox.pokeByteIndex off arr val \
122 >> let sz = Unbox.sizeOf (Proxy :: Proxy _type) \
125DERIVE_SERIALIZE_FROM_UNBOX(Bool)
126DERIVE_SERIALIZE_FROM_UNBOX(Char)
127DERIVE_SERIALIZE_FROM_UNBOX(Int8)
128DERIVE_SERIALIZE_FROM_UNBOX(Int16)
129DERIVE_SERIALIZE_FROM_UNBOX(Int32)
130DERIVE_SERIALIZE_FROM_UNBOX(Int)
131DERIVE_SERIALIZE_FROM_UNBOX(Int64)
132DERIVE_SERIALIZE_FROM_UNBOX(Word)
133DERIVE_SERIALIZE_FROM_UNBOX(Word8)
134DERIVE_SERIALIZE_FROM_UNBOX(Word16)
135DERIVE_SERIALIZE_FROM_UNBOX(Word32)
136DERIVE_SERIALIZE_FROM_UNBOX(Word64)
137DERIVE_SERIALIZE_FROM_UNBOX(Double)
138DERIVE_SERIALIZE_FROM_UNBOX(Float)
139DERIVE_SERIALIZE_FROM_UNBOX((StablePtr a))
140DERIVE_SERIALIZE_FROM_UNBOX((Ptr a))
141DERIVE_SERIALIZE_FROM_UNBOX((FunPtr a))
143instance forall a. Serialize a => Serialize [a] where
146 size = VarSize $ \lst ->
147 case size :: Size a of
150 (\acc x -> acc + f x)
151 (Unbox.sizeOf (Proxy :: Proxy Int))
156 + Unbox.sizeOf (Proxy :: Proxy Int)
158 {-# INLINE deserialize #-}
159 deserialize off arr = do
160 len <- Unbox.peekByteIndex off arr :: IO Int
161 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Int)
162 let peekList buf o 0 = pure (o, reverse buf)
163 peekList buf o i = do
164 (o1, x) <- deserialize o arr
165 peekList (x:buf) o1 (i - 1)
168 {-# INLINE serialize #-}
169 serialize off arr val = do
170 void $ serialize off arr (length val)
171 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Int)
172 let pokeList o [] = pure o
173 pokeList o (x:xs) = do
174 o1 <- serialize o arr x
178--------------------------------------------------------------------------------
179-- High level functions
180--------------------------------------------------------------------------------
182{-# INLINE encodeAs #-}
183encodeAs :: forall a. Serialize a => PinnedState -> a -> Array Word8
187 case size :: Size a of
190 -- We encode the length of the encoding as a header hence the 8 extra
191 -- bytes to encode Int64
192 mbarr <- Unbox.newBytesAs ps (8 + len)
193 off1 <- serialize 0 mbarr (fromIntegral len :: Int64)
194 off2 <- serialize off1 mbarr a
195 assertM(off2 == len + off1)
196 pure $ Array mbarr 0 len
199encode :: Serialize a => a -> Array Word8
200encode = encodeAs Unpinned
202{-# INLINE pinnedEncode #-}
203pinnedEncode :: Serialize a => a -> Array Word8
204pinnedEncode = encodeAs Pinned
207decode :: Serialize a => Array Word8 -> a
208decode arr@(Array {..}) = unsafeInlineIO $ do
209 let lenArr = Array.length arr
210 (off1, lenEncoding :: Int64) <- deserialize 0 arrContents
211 (off2, val) <- deserialize off1 arrContents
212 assertM(fromIntegral lenEncoding + off1 == off2)
213 assertM(lenArr == off2)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list