-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathPut.hs
More file actions
395 lines (319 loc) · 11.2 KB
/
Put.hs
File metadata and controls
395 lines (319 loc) · 11.2 KB
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE Safe #-}
#if MIN_VERSION_base(4,9,0)
#define HAS_SEMIGROUP
#endif
-----------------------------------------------------------------------------
-- |
-- Module : Data.Binary.Put
-- Copyright : Lennart Kolmodin
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Lennart Kolmodin <kolmodin@gmail.com>
-- Stability : stable
-- Portability : Portable to Hugs and GHC. Requires MPTCs
--
-- The Put monad. A monad for efficiently constructing lazy bytestrings.
--
-----------------------------------------------------------------------------
module Data.Binary.Put (
-- * The Put type
Put
, PutM(..)
, runPut
, runPutM
, putBuilder
, execPut
-- * Flushing the implicit parse state
, flush
-- * Primitives
, putWord8
, putInt8
, putByteString
, putLazyByteString
, putShortByteString
-- * Big-endian primitives
, putWord16be
, putWord32be
, putWord64be
, putInt16be
, putInt32be
, putInt64be
, putFloatbe
, putDoublebe
-- * Little-endian primitives
, putWord16le
, putWord32le
, putWord64le
, putInt16le
, putInt32le
, putInt64le
, putFloatle
, putDoublele
-- * Host-endian, unaligned writes
, putWordhost -- :: Word -> Put
, putWord16host -- :: Word16 -> Put
, putWord32host -- :: Word32 -> Put
, putWord64host -- :: Word64 -> Put
, putInthost -- :: Int -> Put
, putInt16host -- :: Int16 -> Put
, putInt32host -- :: Int32 -> Put
, putInt64host -- :: Int64 -> Put
, putFloathost
, putDoublehost
-- * Unicode
, putCharUtf8
, putStringUtf8
) where
import qualified Data.Monoid as Monoid
import Data.Binary.Builder (Builder, toLazyByteString)
import qualified Data.Binary.Builder as B
import Data.Int
import Data.Word
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import Data.ByteString.Short
#ifdef HAS_SEMIGROUP
import Data.Semigroup
#endif
import Control.Applicative
import Prelude -- Silence AMP warning.
-- needed for casting Floats/Doubles to words.
import Data.Binary.FloatCast (floatToWord, doubleToWord)
------------------------------------------------------------------------
-- XXX Strict in buffer only.
data PairS a = PairS a !Builder
sndS :: PairS a -> Builder
sndS (PairS _ b) = b
-- | The PutM type. A Writer monad over the efficient Builder monoid.
newtype PutM a = Put { unPut :: PairS a }
-- | Put merely lifts Builder into a Writer monad, applied to ().
type Put = PutM ()
instance Functor PutM where
fmap f m = Put $ let PairS a w = unPut m in PairS (f a) w
{-# INLINE fmap #-}
instance Applicative PutM where
pure a = Put $ PairS a Monoid.mempty
{-# INLINE pure #-}
m <*> k = Put $
let PairS f w = unPut m
PairS x w' = unPut k
in PairS (f x) (w `Monoid.mappend` w')
m *> k = Put $
let PairS _ w = unPut m
PairS b w' = unPut k
in PairS b (w `Monoid.mappend` w')
{-# INLINE (*>) #-}
-- Standard Writer monad, with aggressive inlining
instance Monad PutM where
m >>= k = Put $
let PairS a w = unPut m
PairS b w' = unPut (k a)
in PairS b (w `Monoid.mappend` w')
{-# INLINE (>>=) #-}
return = pure
{-# INLINE return #-}
(>>) = (*>)
{-# INLINE (>>) #-}
instance Monoid.Monoid (PutM ()) where
mempty = pure ()
{-# INLINE mempty #-}
#ifdef HAS_SEMIGROUP
mappend = (<>)
#else
mappend = mappend'
#endif
{-# INLINE mappend #-}
mappend' :: Put -> Put -> Put
mappend' m k = Put $
let PairS _ w = unPut m
PairS _ w' = unPut k
in PairS () (w `Monoid.mappend` w')
{-# INLINE mappend' #-}
#ifdef HAS_SEMIGROUP
instance Semigroup (PutM ()) where
(<>) = mappend'
{-# INLINE (<>) #-}
#endif
tell :: Builder -> Put
tell b = Put $ PairS () b
{-# INLINE tell #-}
putBuilder :: Builder -> Put
putBuilder = tell
{-# INLINE putBuilder #-}
-- | Run the 'Put' monad
execPut :: PutM a -> Builder
execPut = sndS . unPut
{-# INLINE execPut #-}
-- | Run the 'Put' monad with a serialiser
runPut :: Put -> L.ByteString
runPut = toLazyByteString . sndS . unPut
{-# INLINE runPut #-}
-- | Run the 'Put' monad with a serialiser and get its result
runPutM :: PutM a -> (a, L.ByteString)
runPutM (Put (PairS f s)) = (f, toLazyByteString s)
{-# INLINE runPutM #-}
------------------------------------------------------------------------
-- | Pop the ByteString we have constructed so far, if any, yielding a
-- new chunk in the result ByteString.
flush :: Put
flush = tell B.flush
{-# INLINE flush #-}
-- | Efficiently write a byte into the output buffer
putWord8 :: Word8 -> Put
putWord8 = tell . B.singleton
{-# INLINE putWord8 #-}
-- | Efficiently write a signed byte into the output buffer
putInt8 :: Int8 -> Put
putInt8 = tell . B.singleton . fromIntegral
{-# INLINE putInt8 #-}
-- | An efficient primitive to write a strict ByteString into the output buffer.
-- It flushes the current buffer, and writes the argument into a new chunk.
putByteString :: S.ByteString -> Put
putByteString = tell . B.fromByteString
{-# INLINE putByteString #-}
-- | Write a lazy ByteString efficiently, simply appending the lazy
-- ByteString chunks to the output buffer
putLazyByteString :: L.ByteString -> Put
putLazyByteString = tell . B.fromLazyByteString
{-# INLINE putLazyByteString #-}
-- | Write 'ShortByteString' to the buffer
putShortByteString :: ShortByteString -> Put
putShortByteString = tell . B.fromShortByteString
{-# INLINE putShortByteString #-}
-- | Write a Word16 in big endian format
putWord16be :: Word16 -> Put
putWord16be = tell . B.putWord16be
{-# INLINE putWord16be #-}
-- | Write a Word16 in little endian format
putWord16le :: Word16 -> Put
putWord16le = tell . B.putWord16le
{-# INLINE putWord16le #-}
-- | Write a Word32 in big endian format
putWord32be :: Word32 -> Put
putWord32be = tell . B.putWord32be
{-# INLINE putWord32be #-}
-- | Write a Word32 in little endian format
putWord32le :: Word32 -> Put
putWord32le = tell . B.putWord32le
{-# INLINE putWord32le #-}
-- | Write a Word64 in big endian format
putWord64be :: Word64 -> Put
putWord64be = tell . B.putWord64be
{-# INLINE putWord64be #-}
-- | Write a Word64 in little endian format
putWord64le :: Word64 -> Put
putWord64le = tell . B.putWord64le
{-# INLINE putWord64le #-}
-- | Write an Int16 in big endian format
putInt16be :: Int16 -> Put
putInt16be = tell . B.putInt16be
{-# INLINE putInt16be #-}
-- | Write an Int16 in little endian format
putInt16le :: Int16 -> Put
putInt16le = tell . B.putInt16le
{-# INLINE putInt16le #-}
-- | Write an Int32 in big endian format
putInt32be :: Int32 -> Put
putInt32be = tell . B.putInt32be
{-# INLINE putInt32be #-}
-- | Write an Int32 in little endian format
putInt32le :: Int32 -> Put
putInt32le = tell . B.putInt32le
{-# INLINE putInt32le #-}
-- | Write an Int64 in big endian format
putInt64be :: Int64 -> Put
putInt64be = tell . B.putInt64be
{-# INLINE putInt64be #-}
-- | Write an Int64 in little endian format
putInt64le :: Int64 -> Put
putInt64le = tell . B.putInt64le
{-# INLINE putInt64le #-}
------------------------------------------------------------------------
-- | /O(1)./ Write a single native machine word. The word is
-- written 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 written this way are not portable to
-- different endian or word sized machines, without conversion.
--
putWordhost :: Word -> Put
putWordhost = tell . B.putWordhost
{-# INLINE putWordhost #-}
-- | /O(1)./ Write a Word16 in native host order and host endianness.
-- For portability issues see @putWordhost@.
putWord16host :: Word16 -> Put
putWord16host = tell . B.putWord16host
{-# INLINE putWord16host #-}
-- | /O(1)./ Write a Word32 in native host order and host endianness.
-- For portability issues see @putWordhost@.
putWord32host :: Word32 -> Put
putWord32host = tell . B.putWord32host
{-# INLINE putWord32host #-}
-- | /O(1)./ Write a Word64 in native host order
-- On a 32 bit machine we write two host order Word32s, in big endian form.
-- For portability issues see @putWordhost@.
putWord64host :: Word64 -> Put
putWord64host = tell . B.putWord64host
{-# INLINE putWord64host #-}
-- | /O(1)./ Write a single native machine word. The word is
-- written 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 written this way are not portable to
-- different endian or word sized machines, without conversion.
--
putInthost :: Int -> Put
putInthost = tell . B.putInthost
{-# INLINE putInthost #-}
-- | /O(1)./ Write an Int16 in native host order and host endianness.
-- For portability issues see @putInthost@.
putInt16host :: Int16 -> Put
putInt16host = tell . B.putInt16host
{-# INLINE putInt16host #-}
-- | /O(1)./ Write an Int32 in native host order and host endianness.
-- For portability issues see @putInthost@.
putInt32host :: Int32 -> Put
putInt32host = tell . B.putInt32host
{-# INLINE putInt32host #-}
-- | /O(1)./ Write an Int64 in native host order
-- On a 32 bit machine we write two host order Int32s, in big endian form.
-- For portability issues see @putInthost@.
putInt64host :: Int64 -> Put
putInt64host = tell . B.putInt64host
{-# INLINE putInt64host #-}
------------------------------------------------------------------------
-- Floats/Doubles
-- | Write a 'Float' in big endian IEEE-754 format.
putFloatbe :: Float -> Put
putFloatbe = putWord32be . floatToWord
{-# INLINE putFloatbe #-}
-- | Write a 'Float' in little endian IEEE-754 format.
putFloatle :: Float -> Put
putFloatle = putWord32le . floatToWord
{-# INLINE putFloatle #-}
-- | Write a 'Float' in native in IEEE-754 format and host endian.
putFloathost :: Float -> Put
putFloathost = putWord32host . floatToWord
{-# INLINE putFloathost #-}
-- | Write a 'Double' in big endian IEEE-754 format.
putDoublebe :: Double -> Put
putDoublebe = putWord64be . doubleToWord
{-# INLINE putDoublebe #-}
-- | Write a 'Double' in little endian IEEE-754 format.
putDoublele :: Double -> Put
putDoublele = putWord64le . doubleToWord
{-# INLINE putDoublele #-}
-- | Write a 'Double' in native in IEEE-754 format and host endian.
putDoublehost :: Double -> Put
putDoublehost = putWord64host . doubleToWord
{-# INLINE putDoublehost #-}
------------------------------------------------------------------------
-- Unicode
-- | Write a character using UTF-8 encoding.
putCharUtf8 :: Char -> Put
putCharUtf8 = tell . B.putCharUtf8
{-# INLINE putCharUtf8 #-}
-- | Write a String using UTF-8 encoding.
putStringUtf8 :: String -> Put
putStringUtf8 = tell . B.putStringUtf8
{-# INLINE putStringUtf8 #-}