1{-# OPTIONS_GHC -fno-warn-orphans #-}
2{-# LANGUAGE UnboxedTuples #-}
4module Streamly.Internal.Data.Unboxed
11 , MutableByteArray(..)
14 , getMutableByteArray#
18 , newAlignedPinnedBytes
24#include "ArrayMacros.h"
26import Data.Complex (Complex((:+)))
27import Data.Functor ((<&>))
28import Data.Functor.Const (Const(..))
29import Data.Functor.Identity (Identity(..))
30import Foreign.Ptr (IntPtr(..), WordPtr(..))
31import Foreign.Storable (Storable(..))
32import GHC.Base (IO(..))
33import GHC.Fingerprint.Type (Fingerprint(..))
34import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))
35import GHC.Real (Ratio(..))
36import GHC.Stable (StablePtr(..))
37import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))
38#if MIN_VERSION_base(4,15,0)
39import GHC.RTS.Flags (IoSubSystem(..))
41import System.IO.Unsafe (unsafePerformIO)
45--------------------------------------------------------------------------------
46-- The ArrayContents type
47--------------------------------------------------------------------------------
49-- XXX can use UnliftedNewtypes
50data MutableByteArray a = MutableByteArray !(MutableByteArray# RealWorld)
52{-# INLINE getMutableByteArray# #-}
53getMutableByteArray# :: MutableByteArray a -> MutableByteArray# RealWorld
54getMutableByteArray# (MutableByteArray mbarr) = mbarr
56{-# INLINE castContents #-}
57castContents :: MutableByteArray a -> MutableByteArray b
58castContents (MutableByteArray mbarr) = MutableByteArray mbarr
61touch :: MutableByteArray a -> IO ()
62touch (MutableByteArray contents) =
63 IO $ \s -> case touch# contents s of s' -> (# s', () #)
65--------------------------------------------------------------------------------
67--------------------------------------------------------------------------------
70nil :: MutableByteArray a
71nil = unsafePerformIO $ newUnpinnedBytes 0
73{-# INLINE newUnpinnedBytes #-}
74newUnpinnedBytes :: Int -> IO (MutableByteArray a)
75newUnpinnedBytes nbytes | nbytes < 0 =
76 errorWithoutStackTrace "newUnpinnedBytes: size must be >= 0"
77newUnpinnedBytes (I# nbytes) = IO $ \s ->
78 case newByteArray# nbytes s of
80 let c = MutableByteArray mbarr#
83{-# INLINE newAlignedPinnedBytes #-}
84newAlignedPinnedBytes :: Int -> Int -> IO (MutableByteArray a)
85newAlignedPinnedBytes nbytes _align | nbytes < 0 =
86 errorWithoutStackTrace "newAlignedPinnedBytes: size must be >= 0"
87newAlignedPinnedBytes (I# nbytes) (I# align) = IO $ \s ->
88 case newAlignedPinnedByteArray# nbytes align s of
90 let c = MutableByteArray mbarr#
93-------------------------------------------------------------------------------
95-------------------------------------------------------------------------------
97{-# INLINE isPinned #-}
98isPinned :: MutableByteArray a -> Bool
99isPinned (MutableByteArray arr#) =
100 let pinnedInt = I# (isMutableByteArrayPinned# arr#)
104{-# INLINE cloneMutableArrayWith# #-}
105cloneMutableArrayWith#
106 :: (Int# -> State# RealWorld -> (# State# RealWorld
107 , MutableByteArray# RealWorld #))
108 -> MutableByteArray# RealWorld
110 -> (# State# RealWorld, MutableByteArray# RealWorld #)
111cloneMutableArrayWith# alloc# arr# s# =
112 case getSizeofMutableByteArray# arr# s# of
114 case alloc# i# s1# of
116 case copyMutableByteArray# arr# 0# arr1# 0# i# s2# of
117 s3# -> (# s3#, arr1# #)
120pin :: MutableByteArray a -> IO (MutableByteArray a)
121pin arr@(MutableByteArray marr#) =
126 case cloneMutableArrayWith# newPinnedByteArray# marr# s# of
127 (# s1#, marr1# #) -> (# s1#, MutableByteArray marr1# #)
130unpin :: MutableByteArray a -> IO (MutableByteArray a)
131unpin arr@(MutableByteArray marr#) =
132 if not (isPinned arr)
136 case cloneMutableArrayWith# newByteArray# marr# s# of
137 (# s1#, marr1# #) -> (# s1#, MutableByteArray marr1# #)
139--------------------------------------------------------------------------------
140-- The Unboxed type class
141--------------------------------------------------------------------------------
143-- In theory we could convert a type to and from a byte stream and use that
144-- to implement boxing, unboxing. But that would be inefficient. This type
145-- class allows each primitive type to have its own specific efficient
146-- implementation to read and write the type to memory.
148-- This is a typeclass that uses MutableByteArray but could use ForeignPtr or
149-- any other representation of memory. We could make this a multiparameter type
150-- class if necessary.
152-- If the type class would have to support reading and writing to a Ptr as well,
153-- basically what Storable does. We will also need to touch the anchoring ptr at
154-- the right points which is prone to errors. However, it should be simple to
155-- implement unmanaged/read-only memory arrays by allowing a Ptr type in
156-- ArrayContents, though it would require all instances to support reading from
159-- There is a reason for using byte offset instead of element index in read and
160-- write operations in the type class. If we use element index slicing of the
161-- array becomes rigid. We can only slice the array at addresses that are
162-- aligned with the type, therefore, we cannot slice at misaligned location and
163-- then cast the slice into another type which does not ncessarily align with
166-- As a side note, there seem to be no performance advantage of alignment
168-- https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/
170-- We have to keep the instances compatible with Storable as we depend on it for
171-- size and alignment. We can possibly write tests for checking compatibility
174-- | An 'Unbox' type supplies operations for reading and writing the type
175-- from and to a byte array in memory. The read operation constructs the boxed
176-- type from an unboxed byte representation in memory. The write operation
177-- writes the boxed type to an unboxed byte representation in memory.
179-- This type class dependes on Storable for size and alignment of the type. It
180-- is similar to Storable in the sense that it has corresponding operations for
181-- peekByteOff and pokeByteOff. While Storable allows writing to a Ptr, this
182-- type class allows writing to a MutableByteArray#.
183class Storable a => Unbox a where
184 -- | Read an element of type "a" from a MutableByteArray given the byte
186 box :: MutableByteArray a -> Int -> IO a
187 -- | Write an element of type "a" to a MutableByteArray given the byte
189 unbox :: MutableByteArray a -> Int -> a -> IO ()
191type Unboxed a = (Unbox a, Storable a)
193#define DERIVE_UNBOXED(_type, _constructor, _readArray, _writeArray) \
194instance Unbox _type where { \
195; {-# INLINE box #-} \
196; box (MutableByteArray mbarr) (I# n) = IO $ \s -> \
197 case _readArray mbarr n s of \
198 { (# s1, i #) -> (# s1, _constructor i #) } \
199; {-# INLINE unbox #-} \
200; unbox (MutableByteArray mbarr) (I# n) (_constructor val) = \
201 IO $ \s -> (# _writeArray mbarr n val s, () #) \
204#define DERIVE_WRAPPED_UNBOX(_constraint, _type, _constructor) \
205instance _constraint Unbox _type where \
206; {-# INLINE box #-} \
207; box arr i = _constructor <$> box (castContents arr) i \
208; {-# INLINE unbox #-} \
209; unbox arr i (_constructor a) = unbox (castContents arr) i a
211#define DERIVE_BINARY_UNBOX(_constraint, _type, _constructor, _innerType) \
212instance _constraint Unbox _type where { \
213; {-# INLINE box #-} \
215 let contents = castContents arr :: MutableByteArray _innerType \
216 in box contents i >>= \
217 (\p1 -> box contents (i + SIZE_OF(_innerType)) \
218 <&> _constructor p1) \
219; {-# INLINE unbox #-} \
220; unbox arr i (_constructor p1 p2) = \
221 let contents = castContents arr :: MutableByteArray _innerType \
222 in unbox contents i p1 >> \
223 unbox contents (i + SIZE_OF(_innerType)) p2 \
228 , readWord8ArrayAsWideChar#
229 , writeWord8ArrayAsWideChar#)
238 , readWord8ArrayAsInt16#
239 , writeWord8ArrayAsInt16#)
243 , readWord8ArrayAsInt32#
244 , writeWord8ArrayAsInt32#)
248 , readWord8ArrayAsInt#
249 , writeWord8ArrayAsInt#)
253 , readWord8ArrayAsInt64#
254 , writeWord8ArrayAsInt64#)
258 , readWord8ArrayAsWord#
259 , writeWord8ArrayAsWord#)
266DERIVE_UNBOXED( Word16
268 , readWord8ArrayAsWord16#
269 , writeWord8ArrayAsWord16#)
271DERIVE_UNBOXED( Word32
273 , readWord8ArrayAsWord32#
274 , writeWord8ArrayAsWord32#)
276DERIVE_UNBOXED( Word64
278 , readWord8ArrayAsWord64#
279 , writeWord8ArrayAsWord64#)
281DERIVE_UNBOXED( Double
283 , readWord8ArrayAsDouble#
284 , writeWord8ArrayAsDouble#)
288 , readWord8ArrayAsFloat#
289 , writeWord8ArrayAsFloat#)
291DERIVE_UNBOXED( (StablePtr a)
293 , readWord8ArrayAsStablePtr#
294 , writeWord8ArrayAsStablePtr#)
296DERIVE_UNBOXED( (Ptr a)
298 , readWord8ArrayAsAddr#
299 , writeWord8ArrayAsAddr#)
301DERIVE_UNBOXED( (FunPtr a)
303 , readWord8ArrayAsAddr#
304 , writeWord8ArrayAsAddr#)
306DERIVE_WRAPPED_UNBOX(,IntPtr,IntPtr)
307DERIVE_WRAPPED_UNBOX(,WordPtr,WordPtr)
308DERIVE_WRAPPED_UNBOX(Unbox a =>,(Identity a),Identity)
309#if MIN_VERSION_base(4,14,0)
310DERIVE_WRAPPED_UNBOX(Unbox a =>,(Down a),Down)
312DERIVE_WRAPPED_UNBOX(Unbox a =>,(Const a b),Const)
313DERIVE_BINARY_UNBOX(forall a. Unboxed a =>,(Complex a),(:+),a)
314DERIVE_BINARY_UNBOX(forall a. (Integral a, Unboxed a) =>,(Ratio a),(:%),a)
315DERIVE_BINARY_UNBOX(,Fingerprint,Fingerprint,Word64)
317instance Unbox () where
323 unbox _ _ _ = return ()
325#if MIN_VERSION_base(4,15,0)
326instance Unbox IoSubSystem where
329 box arr i = toEnum <$> box (castContents arr) i
332 unbox arr i a = unbox (castContents arr) i (fromEnum a)
335instance Unbox Bool where
339 res <- box (castContents arr) i
340 return $ res /= (0 :: Int32)
345 then unbox (castContents arr) i (1 :: Int32)
346 else unbox (castContents arr) i (0 :: Int32)
348--------------------------------------------------------------------------------
350--------------------------------------------------------------------------------
352{-# INLINE peekWith #-}
353peekWith :: Unboxed a => MutableByteArray a -> Int -> IO a
356{-# INLINE pokeWith #-}
357pokeWith :: Unboxed a => MutableByteArray a -> Int -> a -> IO ()
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