@@ -1,3 +1,5 @@
1+{-# LANGUAGE MagicHash #-}
2+
13-----------------------------------------------------------------------------
24-- |
35-- Module : Data.Binary.Bits.Put
@@ -20,8 +22,10 @@ module Data.Binary.Bits.Put
2022 -- * Data types
2123 -- ** Bool
2224 , putBool
25+ , putBool#
2326
2427 -- ** Words
28+ , putWord#
2529 , putWord8
2630 , putWord16
2731 , putWord32
@@ -41,14 +45,16 @@ import qualified Data.Binary.Put as Put
4145import Data.Binary.Put ( Put )
4246import Data.Binary.Bits.Internal
4347import Data.Binary.Bits.BitOrder
48+import Data.Binary.Bits.Alignment
4449
4550import Data.ByteString as BS
4651import Data.ByteString.Unsafe as BS
4752
48−import Control.Applicative
53+import Control.Monad (when)
4954import Data.Bits
50−import Data.Monoid
5155import Data.Word
56+import GHC.Base (Word#, shiftRL#, Int (..), Int#, (-#), (<#), isTrue#, (+#), shiftL#, wordToWord8#, orWord8#)
57+import GHC.Word (Word8(..))
5258
5359data BitPut a = BitPut { run :: (S -> PairS a) }
5460
@@ -60,11 +66,18 @@ data S = S !Builder !Word8 !Int !BitOrder
6066putBool :: Bool -> BitPut ()
6167putBool b = putWord8 1 (if b then 0xff else 0x00)
6268
69+-- | Put a 1 bit 'Bool'.
70+putBool# :: Word# -> BitPut ()
71+putBool# b = putWord# 1# b
72+
6373
6474-- | Generic putWord
6575putWord :: (Num a, FastBits a, Integral a) => Int -> a -> BitPut ()
6676putWord n w = BitPut $ \s -> PairS () (putWordS n w s)
6777
78+putWord# :: Int# -> Word# -> BitPut ()
79+putWord# n w = BitPut $ \s -> PairS () (putWordS# n w s)
80+
6881putWordS :: (Num a, FastBits a, Integral a) => Int -> a -> S -> S
6982putWordS n w s@(S builder b o bo) = s'
7083 where
@@ -83,8 +96,8 @@ putWordS n w s@(S builder b o bo) = s'
8396 w' = case bo of
8497 BB -> w
8598 BL -> w `fastShiftR` cn
86− LB -> w `fastShiftR` cn
87− LL -> w
99+ LL -> w `fastShiftR` cn
100+ LB -> w
88101
89102 -- Select bits to store in the current byte.
90103 -- Put them in the correct order and return them in the least-significant
@@ -92,8 +105,8 @@ putWordS n w s@(S builder b o bo) = s'
92105 selectBits :: (Num a, FastBits a, Integral a) => a -> Word8
93106 selectBits x = fromIntegral $ case bo of
94107 BB -> mask cn $ x `fastShiftR` (n-cn)
95− LL -> reverseBits cn $ mask cn $ x `fastShiftR` (n-cn)
96− LB -> mask cn x
108+ LB -> reverseBits cn $ mask cn $ x `fastShiftR` (n-cn)
109+ LL -> mask cn x
97110 BL -> reverseBits cn $ mask cn x
98111
99112 -- shift left at the correct position
@@ -101,8 +114,55 @@ putWordS n w s@(S builder b o bo) = s'
101114 shl x = case bo of
102115 BB -> x `fastShiftL` (8-o-cn)
103116 BL -> x `fastShiftL` (8-o-cn)
104− LB -> x `fastShiftL` o
105117 LL -> x `fastShiftL` o
118+ LB -> x `fastShiftL` o
119+
120+ flush s2@(S b2 w2 o2 bo2)
121+ | o2 == 8 = S (b2 `mappend` B.singleton w2) 0 0 bo2
122+ | otherwise = s2
123+
124+putWordS# :: Int# -> Word# -> S -> S
125+putWordS# n w s@(S builder (W8# b) (I# o) bo) = s'
126+ where
127+ min# :: Int# -> Int# -> Int#
128+ min# x y | isTrue# (x <# y) = x
129+ min# _ y = y
130+
131+ -- number of bits that will be stored in the current byte
132+ cn = min# (8# -# o) n
133+
134+ -- new state
135+ s' = case n of
136+ 0# -> s
137+ _ -> putWordS# (n -# cn) w' (flush (S builder (W8# b') (I# (o +# cn)) bo))
138+
139+ -- new current byte
140+ b' = wordToWord8# (shl (selectBits w)) `orWord8#` b
141+
142+ -- Word containing the remaining (n-cn) bits to store in its LSB
143+ w' = case bo of
144+ BB -> w
145+ BL -> w `shiftRL#` cn
146+ LL -> w `shiftRL#` cn
147+ LB -> w
148+
149+ -- Select bits to store in the current byte.
150+ -- Put them in the correct order and return them in the least-significant
151+ -- bits of the returned value
152+ selectBits :: Word# -> Word#
153+ selectBits x = case bo of
154+ BB -> mask# cn (x `shiftRL#` (n -# cn))
155+ LB -> reverseBits# cn (mask# cn (x `shiftRL#` (n -# cn)))
156+ LL -> mask# cn x
157+ BL -> reverseBits# cn (mask# cn x)
158+
159+ -- shift left at the correct position
160+ shl :: Word# -> Word#
161+ shl x = case bo of
162+ BB -> x `shiftL#` (8# -# o -# cn)
163+ BL -> x `shiftL#` (8# -# o -# cn)
164+ LL -> x `shiftL#` o
165+ LB -> x `shiftL#` o
106166
107167 flush s2@(S b2 w2 o2 bo2)
108168 | o2 == 8 = S (b2 `mappend` B.singleton w2) 0 0 bo2
@@ -141,9 +201,9 @@ putWord64be = putWord
141201--
142202-- Examples: 3 bits are already written in the current byte
143203-- BB: ABCDEFGH IJKLMNOP -> xxxABCDE FGHIJKLM NOPxxxxx
144−-- LB: ABCDEFGH IJKLMNOP -> LMNOPxxx DEFGHIJK xxxxxABC
204+-- LL: ABCDEFGH IJKLMNOP -> LMNOPxxx DEFGHIJK xxxxxABC
145205-- BL: ABCDEFGH IJKLMNOP -> xxxPONML KJIHGFED CBAxxxxx
146−-- LL: ABCDEFGH IJKLMNOP -> EDCBAxxx MLKJIHGF xxxxxPON
206+-- LB: ABCDEFGH IJKLMNOP -> EDCBAxxx MLKJIHGF xxxxxPON
147207putByteString :: ByteString -> BitPut ()
148208putByteString bs = BitPut $ \s -> PairS () (putByteStringS bs s)
149209
@@ -152,13 +212,13 @@ putByteStringS bs s
152212 | BS.null bs = s
153213 | otherwise = case s of
154214 (S builder b 0 BB) -> S (builder `mappend` B.fromByteString bs) b 0 BB
155− (S builder b 0 LB) -> S (builder `mappend` B.fromByteString (BS.reverse bs)) b 0 LB
156− (S builder b 0 LL) -> S (builder `mappend` B.fromByteString (rev bs)) b 0 LL
215+ (S builder b 0 LL) -> S (builder `mappend` B.fromByteString (BS.reverse bs)) b 0 LL
216+ (S builder b 0 LB) -> S (builder `mappend` B.fromByteString (rev bs)) b 0 LB
157217 (S builder b 0 BL) -> S (builder `mappend` B.fromByteString (rev (BS.reverse bs))) b 0 BL
158218 (S _ _ _ BB) -> putByteStringS (BS.unsafeTail bs) (putWordS 8 (BS.unsafeHead bs) s)
159− (S _ _ _ LB) -> putByteStringS (BS.unsafeInit bs) (putWordS 8 (BS.unsafeLast bs) s)
219+ (S _ _ _ LL) -> putByteStringS (BS.unsafeInit bs) (putWordS 8 (BS.unsafeLast bs) s)
160220 (S _ _ _ BL) -> putByteStringS (BS.unsafeInit bs) (putWordS 8 (BS.unsafeLast bs) s)
161− (S _ _ _ LL) -> putByteStringS (BS.unsafeTail bs) (putWordS 8 (BS.unsafeHead bs) s)
221+ (S _ _ _ LB) -> putByteStringS (BS.unsafeTail bs) (putWordS 8 (BS.unsafeHead bs) s)
162222 where
163223 rev = BS.map (reverseBits 8)
164224
@@ -178,6 +238,9 @@ flushIncomplete s@(S b w o bo)
178238 | o == 0 = s
179239 | otherwise = (S (b `mappend` B.singleton w) 0 0 bo)
180240
241+getOffset :: BitPut Int
242+getOffset = BitPut $ \s@(S _ _ o _) -> PairS o s
243+
181244-- | Run the 'BitPut' monad inside 'Put'.
182245runBitPut :: BitPut () -> Put.Put
183246runBitPut m = Put.putBuilder b
@@ -208,3 +271,15 @@ instance BitOrderable BitPut where
208271 setBitOrder bo = BitPut $ \(S bu b o _) -> PairS () (S bu b o bo)
209272
210273 getBitOrder = BitPut $ \s@(S _ _ _ bo) -> PairS bo s
274+
275+instance Alignable BitPut where
276+ -- | Skip the given number of bits
277+ skipBits n
278+ | n <= 64 = putWord64 n 0
279+ | otherwise = putWord64 64 0 >> skipBits (n-64)
280+
281+ -- | Skip bits if necessary to align to the next byte
282+ alignByte = do
283+ o <- getOffset
284+ when (o /= 0) $
285+ skipBits (8-o)