| 1 | module Repro4 (test) where |
|---|
| 2 | |
|---|
| 3 | import qualified Data.ByteString as S |
|---|
| 4 | import Data.ByteString.Internal (inlinePerformIO) |
|---|
| 5 | import qualified Data.ByteString.Internal as S |
|---|
| 6 | import Data.Monoid |
|---|
| 7 | import Foreign |
|---|
| 8 | |
|---|
| 9 | newtype Builder = Builder { |
|---|
| 10 | runBuilder :: (Buffer -> [S.ByteString]) -> Buffer -> [S.ByteString] |
|---|
| 11 | } |
|---|
| 12 | |
|---|
| 13 | instance Monoid Builder where |
|---|
| 14 | mempty = empty |
|---|
| 15 | {-# INLINE mempty #-} |
|---|
| 16 | mappend = append |
|---|
| 17 | {-# INLINE mappend #-} |
|---|
| 18 | mconcat = foldr mappend mempty |
|---|
| 19 | {-# INLINE mconcat #-} |
|---|
| 20 | |
|---|
| 21 | empty :: Builder |
|---|
| 22 | empty = Builder (\ k b -> b `seq` k b) |
|---|
| 23 | {-# INLINE empty #-} |
|---|
| 24 | |
|---|
| 25 | singleton :: Word8 -> Builder |
|---|
| 26 | singleton = writeN 1 . flip poke |
|---|
| 27 | {-# INLINE singleton #-} |
|---|
| 28 | |
|---|
| 29 | append :: Builder -> Builder -> Builder |
|---|
| 30 | append (Builder f) (Builder g) = Builder (f . g) |
|---|
| 31 | {-# INLINE [0] append #-} |
|---|
| 32 | |
|---|
| 33 | -- Our internal buffer type |
|---|
| 34 | data 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'. |
|---|
| 41 | flush :: Builder |
|---|
| 42 | flush = 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 | |
|---|
| 47 | defaultSize :: Int |
|---|
| 48 | defaultSize = 32 * k - overhead |
|---|
| 49 | where k = 1024 |
|---|
| 50 | overhead = 2 * sizeOf (undefined :: Int) |
|---|
| 51 | |
|---|
| 52 | -- | Sequence an IO operation on the buffer |
|---|
| 53 | unsafeLiftIO :: (Buffer -> IO Buffer) -> Builder |
|---|
| 54 | unsafeLiftIO 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 |
|---|
| 60 | withSize :: (Int -> Builder) -> Builder |
|---|
| 61 | withSize f = Builder $ \ k buf@(Buffer _ _ _ l) -> runBuilder (f l) k buf |
|---|
| 62 | |
|---|
| 63 | -- | Ensure that there are at least @n@ many bytes available. |
|---|
| 64 | ensureFree :: Int -> Builder |
|---|
| 65 | ensureFree 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. |
|---|
| 72 | writeN :: Int -> (Ptr Word8 -> IO ()) -> Builder |
|---|
| 73 | writeN n f = ensureFree n `append` unsafeLiftIO (writeNBuffer n f) |
|---|
| 74 | {-# INLINE [0] writeN #-} |
|---|
| 75 | |
|---|
| 76 | writeNBuffer :: Int -> (Ptr Word8 -> IO ()) -> Buffer -> IO Buffer |
|---|
| 77 | writeNBuffer 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 | |
|---|
| 82 | newBuffer :: Int -> IO Buffer |
|---|
| 83 | newBuffer 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. |
|---|
| 107 | test :: Int -> Builder |
|---|
| 108 | test 0 = mempty |
|---|
| 109 | test n = singleton 1 `mappend` |
|---|
| 110 | (singleton 2 `mappend` |
|---|
| 111 | (singleton 3 `mappend` |
|---|
| 112 | (singleton 4 `mappend` test (n-4)))) |
|---|