Ticket #4978: Repro4.hs

File Repro4.hs, 3.4 KB (added by tibbe, 2 years ago)
Line 
1module Repro4 (test) where
2
3import qualified Data.ByteString as S
4import Data.ByteString.Internal (inlinePerformIO)
5import qualified Data.ByteString.Internal as S
6import Data.Monoid
7import Foreign
8
9newtype Builder = Builder {
10        runBuilder :: (Buffer -> [S.ByteString]) -> Buffer -> [S.ByteString]
11    }
12
13instance Monoid Builder where
14    mempty  = empty
15    {-# INLINE mempty #-}
16    mappend = append
17    {-# INLINE mappend #-}
18    mconcat = foldr mappend mempty
19    {-# INLINE mconcat #-}
20
21empty :: Builder
22empty = Builder (\ k b -> b `seq` k b)
23{-# INLINE empty #-}
24
25singleton :: Word8 -> Builder
26singleton = writeN 1 . flip poke
27{-# INLINE singleton #-}
28
29append :: Builder -> Builder -> Builder
30append (Builder f) (Builder g) = Builder (f . g)
31{-# INLINE [0] append #-}
32
33-- Our internal buffer type
34data Buffer = Buffer {-# UNPACK #-} !(ForeignPtr Word8)
35                     {-# UNPACK #-} !Int                -- offset
36                     {-# UNPACK #-} !Int                -- used bytes
37                     {-# UNPACK #-} !Int                -- length left
38
39-- | /O(1)./ Pop the 'S.ByteString' we have constructed so far, if any,
40-- yielding a new chunk in the result lazy 'L.ByteString'.
41flush :: Builder
42flush = Builder $ \ k buf@(Buffer p o u l) ->
43    if u == 0
44      then k buf
45      else S.PS p o u : k (Buffer p (o+u) 0 l)
46
47defaultSize :: Int
48defaultSize = 32 * k - overhead
49    where k = 1024
50          overhead = 2 * sizeOf (undefined :: Int)
51
52-- | Sequence an IO operation on the buffer
53unsafeLiftIO :: (Buffer -> IO Buffer) -> Builder
54unsafeLiftIO f =  Builder $ \ k buf -> inlinePerformIO $ do
55    buf' <- f buf
56    return (k buf')
57{-# INLINE unsafeLiftIO #-}
58
59-- | Get the size of the buffer
60withSize :: (Int -> Builder) -> Builder
61withSize f = Builder $ \ k buf@(Buffer _ _ _ l) -> runBuilder (f l) k buf
62
63-- | Ensure that there are at least @n@ many bytes available.
64ensureFree :: Int -> Builder
65ensureFree n = n `seq` withSize $ \ l ->
66    if n <= l then empty else
67        flush `append` unsafeLiftIO (const (newBuffer (max n defaultSize)))
68{-# INLINE [0] ensureFree #-}
69
70-- | Ensure that @n@ many bytes are available, and then use @f@ to write some
71-- bytes into the memory.
72writeN :: Int -> (Ptr Word8 -> IO ()) -> Builder
73writeN n f = ensureFree n `append` unsafeLiftIO (writeNBuffer n f)
74{-# INLINE [0] writeN #-}
75
76writeNBuffer :: Int -> (Ptr Word8 -> IO ()) -> Buffer -> IO Buffer
77writeNBuffer n f (Buffer fp o u l) = do
78    withForeignPtr fp (\p -> f (p `plusPtr` (o+u)))
79    return (Buffer fp o (u+n) (l-n))
80{-# INLINE writeNBuffer #-}
81
82newBuffer :: Int -> IO Buffer
83newBuffer size = do
84    fp <- S.mallocByteString size
85    return $! Buffer fp 0 0 size
86{-# INLINE newBuffer #-}
87
88-- Merge buffer bounds checks.
89{-# RULES
90"append/writeN" forall a b (f::Ptr Word8 -> IO ())
91                           (g::Ptr Word8 -> IO ()) ws.
92    append (writeN a f) (append (writeN b g) ws) =
93        append (writeN (a+b) (\p -> f p >> g (p `plusPtr` a))) ws
94
95"writeN/writeN" forall a b (f::Ptr Word8 -> IO ())
96                           (g::Ptr Word8 -> IO ()).
97    append (writeN a f) (writeN b g) =
98        writeN (a+b) (\p -> f p >> g (p `plusPtr` a))
99
100"ensureFree/ensureFree" forall a b .
101    append (ensureFree a) (ensureFree b) = ensureFree (max a b)
102 #-}
103
104-- Test case
105
106-- Argument must be a multiple of 4.
107test :: Int -> Builder
108test 0 = mempty
109test n = singleton 1 `mappend`
110         (singleton 2 `mappend`
111          (singleton 3 `mappend`
112           (singleton 4 `mappend` test (n-4))))