File: Extra.hs

package info (click to toggle)
ghc 9.10.3-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 169,076 kB
  • sloc: haskell: 713,554; ansic: 84,184; cpp: 30,255; javascript: 9,003; sh: 7,870; fortran: 3,527; python: 3,228; asm: 2,523; makefile: 2,324; yacc: 1,570; lisp: 532; xml: 196; perl: 111; csh: 2
file content (209 lines) | stat: -rw-r--r-- 7,031 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
{-# LANGUAGE Trustworthy #-}

-----------------------------------------------------------------------------
-- | Copyright : (c) 2010      Jasper Van der Jeugt
--               (c) 2010-2011 Simon Meier
-- License     : BSD3-style (see LICENSE)
--
-- Maintainer  : Simon Meier <iridcode@gmail.com>
-- Portability : GHC
--
-- Extra functions for creating and executing 'Builder's. They are intended
-- for application-specific fine-tuning the performance of 'Builder's.
--
-----------------------------------------------------------------------------
module Data.ByteString.Builder.Extra
    (
    -- * Execution strategies
      toLazyByteStringWith
    , AllocationStrategy
    , safeStrategy
    , untrimmedStrategy
    , smallChunkSize
    , defaultChunkSize

    -- * Controlling chunk boundaries
    , byteStringCopy
    , byteStringInsert
    , byteStringThreshold

    , lazyByteStringCopy
    , lazyByteStringInsert
    , lazyByteStringThreshold

    , flush

    -- * Low level execution
    , BufferWriter
    , Next(..)
    , runBuilder

    -- * Host-specific binary encodings
    , intHost
    , int16Host
    , int32Host
    , int64Host

    , wordHost
    , word16Host
    , word32Host
    , word64Host

    , floatHost
    , doubleHost

    ) where


import Data.ByteString.Builder.Internal
         ( Builder, toLazyByteStringWith
         , AllocationStrategy, safeStrategy, untrimmedStrategy
         , smallChunkSize, defaultChunkSize, flush
         , byteStringCopy, byteStringInsert, byteStringThreshold
         , lazyByteStringCopy, lazyByteStringInsert, lazyByteStringThreshold )

import qualified Data.ByteString.Builder.Internal as I
import qualified Data.ByteString.Builder.Prim  as P
import qualified Data.ByteString.Internal      as S

import Foreign

------------------------------------------------------------------------------
-- Builder execution public API
------------------------------------------------------------------------------

-- | A 'BufferWriter' represents the result of running a 'Builder'.
-- It unfolds as a sequence of chunks of data. These chunks come in two forms:
--
--  * an IO action for writing the Builder's data into a user-supplied memory
--    buffer.
--
--  * a pre-existing chunks of data represented by a 'S.StrictByteString'
--
-- While this is rather low level, it provides you with full flexibility in
-- how the data is written out.
--
-- The 'BufferWriter' itself is an IO action: you supply it with a buffer
-- (as a pointer and length) and it will write data into the buffer.
-- It returns a number indicating how many bytes were actually written
-- (which can be @0@). It also returns a 'Next' which describes what
-- comes next.
--
type BufferWriter = Ptr Word8 -> Int -> IO (Int, Next)

-- | After running a 'BufferWriter' action there are three possibilities for
-- what comes next:
--
data Next =
     -- | This means we're all done. All the builder data has now been written.
     Done

     -- | This indicates that there may be more data to write. It
     -- gives you the next 'BufferWriter' action. You should call that action
     -- with an appropriate buffer. The int indicates the /minimum/ buffer size
     -- required by the next 'BufferWriter' action. That is, if you call the next
     -- action you /must/ supply it with a buffer length of at least this size.
   | More   !Int          BufferWriter

     -- | In addition to the data that has just been written into your buffer
     -- by the 'BufferWriter' action, it gives you a pre-existing chunk
     -- of data as a 'S.StrictByteString'. It also gives you the following 'BufferWriter'
     -- action. It is safe to run this following action using a buffer with as
     -- much free space as was left by the previous run action.
   | Chunk  !S.StrictByteString BufferWriter

-- | Turn a 'Builder' into its initial 'BufferWriter' action.
--
runBuilder :: Builder -> BufferWriter
runBuilder = run . I.runBuilder
  where
    bytesWritten startPtr endPtr = endPtr `minusPtr` startPtr

    run :: I.BuildStep () -> BufferWriter
    run step = \buf len ->
      let doneH endPtr () =
            let !wc  = bytesWritten buf endPtr
                next = Done
             in return (wc, next)

          bufferFullH endPtr minReq step' =
            let !wc  = bytesWritten buf endPtr
                next = More minReq (run step')
             in return (wc, next)

          insertChunkH endPtr bs step' =
            let !wc  = bytesWritten buf endPtr
                next = Chunk bs (run step')
             in return (wc, next)

          br = I.BufferRange buf (buf `plusPtr` len)

      in I.fillWithBuildStep step doneH bufferFullH insertChunkH br



------------------------------------------------------------------------------
-- Host-specific encodings
------------------------------------------------------------------------------

-- | Encode a single native machine 'Int'. The 'Int' is encoded in host order,
-- host endian form, for the machine you're on. On a 64 bit machine the 'Int'
-- is an 8 byte value, on a 32 bit machine, 4 bytes. Values encoded this way
-- are not portable to different endian or int sized machines, without
-- conversion.
--
{-# INLINE intHost #-}
intHost :: Int -> Builder
intHost = P.primFixed P.intHost

-- | Encode a 'Int16' in native host order and host endianness.
{-# INLINE int16Host #-}
int16Host :: Int16 -> Builder
int16Host = P.primFixed P.int16Host

-- | Encode a 'Int32' in native host order and host endianness.
{-# INLINE int32Host #-}
int32Host :: Int32 -> Builder
int32Host = P.primFixed P.int32Host

-- | Encode a 'Int64' in native host order and host endianness.
{-# INLINE int64Host #-}
int64Host :: Int64 -> Builder
int64Host = P.primFixed P.int64Host

-- | Encode a single native machine 'Word'. The 'Word' is encoded in host order,
-- host endian form, for the machine you're on. On a 64 bit machine the 'Word'
-- is an 8 byte value, on a 32 bit machine, 4 bytes. Values encoded this way
-- are not portable to different endian or word sized machines, without
-- conversion.
--
{-# INLINE wordHost #-}
wordHost :: Word -> Builder
wordHost = P.primFixed P.wordHost

-- | Encode a 'Word16' in native host order and host endianness.
{-# INLINE word16Host #-}
word16Host :: Word16 -> Builder
word16Host = P.primFixed P.word16Host

-- | Encode a 'Word32' in native host order and host endianness.
{-# INLINE word32Host #-}
word32Host :: Word32 -> Builder
word32Host = P.primFixed P.word32Host

-- | Encode a 'Word64' in native host order and host endianness.
{-# INLINE word64Host #-}
word64Host :: Word64 -> Builder
word64Host = P.primFixed P.word64Host

-- | Encode a 'Float' in native host order. Values encoded this way are not
-- portable to different endian machines, without conversion.
{-# INLINE floatHost #-}
floatHost :: Float -> Builder
floatHost = P.primFixed P.floatHost

-- | Encode a 'Double' in native host order.
{-# INLINE doubleHost #-}
doubleHost :: Double -> Builder
doubleHost = P.primFixed P.doubleHost