{-# LANGUAGE BangPatterns , CPP , FlexibleContexts , FlexibleInstances , ScopedTypeVariables , UndecidableInstances , UnicodeSyntax #-} -- | Fast, packed, lazy bit streams (i.e. list of 'Bool's) with -- semi-automatic stream fusion. -- -- This module is intended to be imported @qualified@, to avoid name -- clashes with "Prelude" functions. e.g. -- -- > import qualified Data.Bitstream.Lazy as LS -- -- Lazy 'Bitstream's are made of possibly infinite list of strict -- 'SB.Bitstream's as chunks, and each chunks have at least 1 bit. module Data.Bitstream.Lazy ( -- * Data types Bitstream , Left , Right -- * Introducing and eliminating 'Bitstream's , empty , (∅) , singleton , pack , unpack , fromChunks , toChunks -- ** Converting from\/to lazy 'LS.ByteString's , fromByteString , toByteString -- ** Converting from\/to 'Bits'' , fromBits , fromNBits , toBits -- ** Converting from\/to 'S.Stream's , stream , unstream -- * Changing bit order in octets , directionLToR , directionRToL -- * Basic interface , cons , cons' , snoc , append , (⧺) , head , last , tail , init , null , length -- * Transforming 'Bitstream's , map , reverse -- * Reducing 'Bitstream's , foldl , foldl' , foldl1 , foldl1' , foldr , foldr1 -- ** Special folds , concat , concatMap , and , or , any , all -- * Building 'Bitstream's -- ** Scans , scanl , scanl1 , scanr , scanr1 -- ** Replications , iterate , repeat , replicate , cycle -- ** Unfolding , unfoldr , unfoldrN -- * Substreams , take , drop , takeWhile , dropWhile , span , break -- * Searching streams -- ** Searching by equality , elem , (∈) , (∋) , notElem , (∉) , (∌) -- ** Searching with a predicate , find , filter , partition -- ** Indexing streams , (!!) , elemIndex , elemIndices , findIndex , findIndices -- * Zipping and unzipping streams , zip , zip3 , zip4 , zip5 , zip6 , zipWith , zipWith3 , zipWith4 , zipWith5 , zipWith6 , unzip , unzip3 , unzip4 , unzip5 , unzip6 -- * I/O with 'Bitstream's -- ** Standard input and output , getContents , putBits , interact -- ** Files , readFile , writeFile , appendFile -- ** I/O with 'Handle's , hGetContents , hGet , hGetNonBlocking , hPut ) where import qualified Data.Bitstream as SB import Data.Bitstream.Generic hiding (Bitstream) import qualified Data.Bitstream.Generic as G import Data.Bitstream.Internal import Data.Bitstream.Packet import qualified Data.ByteString.Lazy as LS import qualified Data.List as L import Data.Monoid #if MIN_VERSION_vector(0,11,0) import qualified Data.Vector.Fusion.Bundle as S import qualified Data.Vector.Fusion.Bundle.Monadic as B import Data.Vector.Fusion.Bundle (Bundle) import Data.Vector.Fusion.Bundle.Size #else import qualified Data.Vector.Fusion.Stream as S import Data.Vector.Fusion.Stream.Size #endif #if MIN_VERSION_base(4,9,0) import Prelude (Semigroup(..)) #endif import Prelude (seq) import Data.Vector.Fusion.Stream.Monadic (Stream(..), Step(..)) import Data.Vector.Fusion.Util import qualified Data.Vector.Generic as GV import qualified Data.Vector.Generic.New as New import qualified Data.Vector.Generic.Mutable as MVector import qualified Data.Vector.Storable as SV import Prelude ( Bool(..), Eq(..), Int, Integral, Maybe(..) , Monad(..), Num(..), Ord(..), Show(..) , ($), div, error, fmap, otherwise ) import Prelude.Unicode hiding ((⧺), (∈), (∉)) import System.IO (FilePath, Handle, IO) -- 32 KiB * sizeOf (Packet d) == 64 KiB chunkSize ∷ Num α ⇒ α chunkSize = fromInteger (32 ⋅ 1024) {-# INLINE chunkSize #-} chunkBits ∷ Num α ⇒ α chunkBits = chunkSize ⋅ 8 -- | A space-efficient representation of a 'Bool' vector, supporting -- many efficient operations. 'Bitstream's have an idea of -- /directions/ controlling how octets are interpreted as bits. There -- are two types of concrete 'Bitstream's: @'Bitstream' 'Left'@ and -- @'Bitstream' 'Right'@. data Bitstream d = Empty | Chunk {-# UNPACK #-} !(SB.Bitstream d) (Bitstream d) instance Show (Packet d) ⇒ Show (Bitstream d) where {-# INLINEABLE show #-} show ch = L.concat [ "[L: " , L.concat (L.intersperse " " (L.map show (toChunks ch))) , " ]" ] instance G.Bitstream (Bitstream d) ⇒ Eq (Bitstream d) where {-# INLINE (==) #-} x == y = stream x ≡ stream y -- | 'Bitstream's are lexicographically ordered. -- -- @ -- let x = 'pack' ['True' , 'False', 'False'] -- y = 'pack' ['False', 'True' , 'False'] -- z = 'pack' ['False'] -- in -- [ 'compare' x y -- 'GT' -- , 'compare' z y -- 'LT' -- ] -- @ instance G.Bitstream (Bitstream d) ⇒ Ord (Bitstream d) where {-# INLINE compare #-} x `compare` y = stream x `compare` stream y #if MIN_VERSION_base(4,9,0) -- | 'Bitstream' forms 'Semigroup' in the same way as ordinary lists: -- -- @ -- '(<>)' = 'append' -- @ instance G.Bitstream (Bitstream d) ⇒ Semigroup (Bitstream d) where (<>) = (⧺) #endif -- | 'Bitstream' forms 'Monoid' in the same way as ordinary lists: -- -- @ -- 'mempty' = 'empty' -- 'mappend' = 'append' -- 'mconcat' = 'concat' -- @ instance G.Bitstream (Bitstream d) ⇒ Monoid (Bitstream d) where mempty = (∅) mappend = (⧺) mconcat = concat instance G.Bitstream (Bitstream Left) where {-# INLINE basicStream #-} basicStream = lazyStream {-# INLINE basicUnstream #-} basicUnstream = lazyUnstream {-# INLINE basicCons #-} basicCons = lazyCons {-# INLINE basicCons' #-} basicCons' = lazyCons' {-# INLINE basicSnoc #-} basicSnoc = lazySnoc {-# INLINE basicAppend #-} basicAppend = lazyAppend {-# INLINE basicTail #-} basicTail = lazyTail {-# INLINE basicInit #-} basicInit = lazyInit {-# INLINE basicMap #-} basicMap = lazyMap {-# INLINE basicReverse #-} basicReverse = lazyReverse {-# INLINE basicConcat #-} basicConcat = lazyConcat {-# INLINE basicScanl #-} basicScanl = lazyScanl {-# INLINE basicTake #-} basicTake = lazyTake {-# INLINE basicDrop #-} basicDrop = lazyDrop {-# INLINE basicTakeWhile #-} basicTakeWhile = lazyTakeWhile {-# INLINE basicDropWhile #-} basicDropWhile = lazyDropWhile {-# INLINE basicFilter #-} basicFilter = lazyFilter {-# INLINE basicFromNBits #-} basicFromNBits = ((unId ∘ unstreamChunks ∘ packChunks) ∘) ∘ lePacketsFromNBits {-# INLINE basicToBits #-} basicToBits = unId ∘ lePacketsToBits ∘ unpackChunks ∘ streamChunks instance G.Bitstream (Bitstream Right) where {-# INLINE basicStream #-} basicStream = lazyStream {-# INLINE basicUnstream #-} basicUnstream = lazyUnstream {-# INLINE basicCons #-} basicCons = lazyCons {-# INLINE basicCons' #-} basicCons' = lazyCons' {-# INLINE basicSnoc #-} basicSnoc = lazySnoc {-# INLINE basicAppend #-} basicAppend = lazyAppend {-# INLINE basicTail #-} basicTail = lazyTail {-# INLINE basicInit #-} basicInit = lazyInit {-# INLINE basicMap #-} basicMap = lazyMap {-# INLINE basicReverse #-} basicReverse = lazyReverse {-# INLINE basicConcat #-} basicConcat = lazyConcat {-# INLINE basicScanl #-} basicScanl = lazyScanl {-# INLINE basicTake #-} basicTake = lazyTake {-# INLINE basicDrop #-} basicDrop = lazyDrop {-# INLINE basicTakeWhile #-} basicTakeWhile = lazyTakeWhile {-# INLINE basicDropWhile #-} basicDropWhile = lazyDropWhile {-# INLINE basicFilter #-} basicFilter = lazyFilter {-# INLINE basicFromNBits #-} basicFromNBits = ((unId ∘ unstreamChunks ∘ packChunks) ∘) ∘ bePacketsFromNBits {-# INLINE basicToBits #-} basicToBits = unId ∘ bePacketsToBits ∘ unpackChunks ∘ streamChunks {-# INLINE lazyStream #-} #if MIN_VERSION_vector(0,11,0) lazyStream ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → Bundle SV.Vector Bool #else lazyStream ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → S.Stream Bool #endif lazyStream = {-# CORE "Lazy Bitstream stream" #-} S.concatMap stream ∘ streamChunks {-# INLINE lazyUnstream #-} #if MIN_VERSION_vector(0,11,0) lazyUnstream ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ Bundle v Bool → Bitstream d #else lazyUnstream ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ S.Stream Bool → Bitstream d #endif lazyUnstream = {-# CORE "Lazy Bitstream unstream" #-} unId ∘ unstreamChunks ∘ packChunks ∘ packPackets lazyCons ∷ G.Bitstream (SB.Bitstream d) ⇒ Bool → Bitstream d → Bitstream d {-# INLINE lazyCons #-} lazyCons = Chunk ∘ singleton lazyCons' ∷ G.Bitstream (SB.Bitstream d) ⇒ Bool → Bitstream d → Bitstream d {-# INLINEABLE lazyCons' #-} lazyCons' b Empty = Chunk (SB.singleton b) Empty lazyCons' b (Chunk x xs) | length x < (chunkBits ∷ Int) = Chunk (b `cons` x) xs | otherwise = Chunk (singleton b) (Chunk x xs) lazySnoc ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ Bitstream d → Bool → Bitstream d {-# INLINEABLE lazySnoc #-} lazySnoc Empty b = Chunk (SB.singleton b) Empty lazySnoc (Chunk x Empty) b | length x < (chunkBits ∷ Int) = Chunk (x `snoc` b) Empty | otherwise = Chunk x (Chunk (singleton b) Empty) lazySnoc (Chunk x xs) b = Chunk x (xs `snoc` b) lazyAppend ∷ G.Bitstream (Bitstream d) ⇒ Bitstream d → Bitstream d → Bitstream d {-# INLINE lazyAppend #-} lazyAppend Empty ch = ch lazyAppend (Chunk x xs) ch = Chunk x (append xs ch) lazyTail ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → Bitstream d {-# INLINEABLE lazyTail #-} lazyTail Empty = emptyStream lazyTail (Chunk x xs) = case tail x of x' | null x' → xs | otherwise → Chunk x' xs lazyInit ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ Bitstream d → Bitstream d {-# INLINEABLE lazyInit #-} lazyInit Empty = emptyStream lazyInit (Chunk x Empty) = case init x of x' | null x' → Empty | otherwise → Chunk x' Empty lazyInit (Chunk x xs ) = Chunk x (init xs) lazyMap ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ (Bool → Bool) → Bitstream d → Bitstream d {-# INLINE lazyMap #-} lazyMap _ Empty = Empty lazyMap f (Chunk x xs) = Chunk (map f x) (map f xs) lazyReverse ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → Bitstream d {-# INLINEABLE lazyReverse #-} lazyReverse ch0 = go ch0 Empty where {-# INLINE go #-} go Empty ch = ch go (Chunk x xs) ch = go xs (Chunk (reverse x) ch) lazyConcat ∷ G.Bitstream (SB.Bitstream d) ⇒ [Bitstream d] → Bitstream d {-# INLINE lazyConcat #-} lazyConcat = fromChunks ∘ L.concatMap toChunks lazyScanl ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ (Bool → Bool → Bool) → Bool → Bitstream d → Bitstream d {-# INLINEABLE lazyScanl #-} lazyScanl f b ch = Chunk (singleton b) (case ch of Empty → Empty Chunk x xs → let h = head x x' = scanl f (f b h) (tail x) l = last x' x'' = init x' xs' = scanl f l xs in if null x'' then xs' else Chunk x'' xs') lazyTake ∷ ( Integral n , G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ n → Bitstream d → Bitstream d {-# INLINEABLE lazyTake #-} lazyTake _ Empty = Empty lazyTake n (Chunk x xs) | n ≤ 0 = Empty | n ≥ length x = Chunk x (take (n - length x) xs) | otherwise = Chunk (take n x) Empty lazyDrop ∷ ( Integral n , G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ n → Bitstream d → Bitstream d {-# INLINEABLE lazyDrop #-} lazyDrop _ Empty = Empty lazyDrop n (Chunk x xs) | n ≤ 0 = Chunk x xs | n ≥ length x = drop (n - length x) xs | otherwise = Chunk (drop n x) xs lazyTakeWhile ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ (Bool → Bool) → Bitstream d → Bitstream d {-# INLINEABLE lazyTakeWhile #-} lazyTakeWhile _ Empty = Empty lazyTakeWhile f (Chunk x xs) = case takeWhile f x of x' | x ≡ x' → Chunk x' (takeWhile f xs) | otherwise → Chunk x' Empty lazyDropWhile ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ (Bool → Bool) → Bitstream d → Bitstream d {-# INLINEABLE lazyDropWhile #-} lazyDropWhile _ Empty = Empty lazyDropWhile f (Chunk x xs) = case dropWhile f x of x' | null x' → dropWhile f xs | otherwise → Chunk x' xs lazyFilter ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Bitstream d) ) ⇒ (Bool → Bool) → Bitstream d → Bitstream d {-# INLINEABLE lazyFilter #-} lazyFilter _ Empty = Empty lazyFilter f (Chunk x xs) = case filter f x of x' | null x' → filter f xs | otherwise → Chunk x' (filter f xs) lazyHead ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → Bool {-# RULES "head → lazyHead" [1] ∀(v ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d). head v = lazyHead v #-} {-# INLINE lazyHead #-} lazyHead Empty = emptyStream lazyHead (Chunk x _) = head x lazyLast ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → Bool {-# RULES "last → lazyLast" [1] ∀(v ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d). last v = lazyLast v #-} {-# INLINE lazyLast #-} lazyLast Empty = emptyStream lazyLast (Chunk x Empty) = last x lazyLast (Chunk _ xs ) = lazyLast xs lazyNull ∷ Bitstream d → Bool {-# RULES "null → lazyNull" [1] null = lazyNull #-} {-# INLINE lazyNull #-} lazyNull Empty = True lazyNull _ = False lazyLength ∷ (G.Bitstream (SB.Bitstream d), Num n) ⇒ Bitstream d → n {-# RULES "length → lazyLength" [1] ∀(v ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d). length v = lazyLength v #-} {-# INLINE lazyLength #-} lazyLength = go 0 where {-# INLINE go #-} go !soFar Empty = soFar go !soFar (Chunk x xs) = go (soFar + length x) xs lazyAnd ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → Bool {-# RULES "and → lazyAnd" [1] ∀(v ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d). and v = lazyAnd v #-} {-# INLINEABLE lazyAnd #-} lazyAnd Empty = False lazyAnd (Chunk x xs) | and x = lazyAnd xs | otherwise = False lazyOr ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → Bool {-# RULES "or → lazyOr" [1] ∀(v ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d). or v = lazyOr v #-} {-# INLINEABLE lazyOr #-} lazyOr Empty = True lazyOr (Chunk x xs) | or x = True | otherwise = lazyOr xs lazyIndex ∷ ( G.Bitstream (SB.Bitstream d) , Integral n , Show n ) ⇒ Bitstream d → n → Bool {-# RULES "(!!) → lazyIndex" [1] ∀(v ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d) n. v !! n = lazyIndex v n #-} {-# INLINEABLE lazyIndex #-} lazyIndex ch0 i0 | i0 < 0 = indexOutOfRange i0 | otherwise = go ch0 i0 where {-# INLINE go #-} go Empty _ = indexOutOfRange i0 go (Chunk x xs) i | i < length x = x !! i | otherwise = go xs (i - length x) emptyStream ∷ α emptyStream = error "Data.Bitstream.Lazy: empty stream" {-# INLINE indexOutOfRange #-} indexOutOfRange ∷ (Integral n, Show n) ⇒ n → α indexOutOfRange n = error ("Data.Bitstream.Lazy: index out of range: " L.++ show n) -- | /O(n)/ Convert a list of chunks, strict 'SB.Bitstream's, into a -- lazy 'Bitstream'. fromChunks ∷ G.Bitstream (SB.Bitstream d) ⇒ [SB.Bitstream d] → Bitstream d {-# INLINE fromChunks #-} fromChunks [] = Empty fromChunks (x:xs) | null x = fromChunks xs | otherwise = Chunk x (fromChunks xs) -- | /O(n)/ Convert a lazy 'Bitstream' into a list of chunks, strict -- 'SB.Bitstream's. toChunks ∷ Bitstream d → [SB.Bitstream d] {-# INLINE toChunks #-} toChunks Empty = [] toChunks (Chunk x xs) = x : toChunks xs -- | /O(n)/ Convert a lazy 'LS.ByteString' into a lazy 'Bitstream'. fromByteString ∷ G.Bitstream (SB.Bitstream d) ⇒ LS.ByteString → Bitstream d {-# INLINE fromByteString #-} fromByteString = fromChunks ∘ L.map SB.fromByteString ∘ LS.toChunks -- | /O(n)/ @'toByteString' bits@ converts a lazy 'Bitstream' @bits@ -- into a lazy 'LS.ByteString'. The resulting octets will be padded -- with zeroes if @bs@ is finite and its 'length' is not multiple of -- 8. toByteString ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ Bitstream d → LS.ByteString {-# INLINE toByteString #-} toByteString = LS.fromChunks ∘ L.map SB.toByteString ∘ toChunks {-# NOINLINE streamChunks #-} #if MIN_VERSION_vector(0,11,0) streamChunks ∷ ( G.Bitstream (SB.Bitstream d) , Monad m ) ⇒ Bitstream d → B.Bundle m v (SB.Bitstream d) streamChunks ch0 = B.fromStream (Stream step ch0) Unknown #else streamChunks ∷ ( G.Bitstream (SB.Bitstream d) , Monad m ) ⇒ Bitstream d → Stream m (SB.Bitstream d) streamChunks ch0 = Stream step ch0 Unknown #endif where {-# INLINE step #-} step Empty = return Done step (Chunk x xs) = return $ Yield x xs {-# NOINLINE unstreamChunks #-} #if MIN_VERSION_vector(0,11,0) unstreamChunks ∷ ( G.Bitstream (SB.Bitstream d) , Monad m ) ⇒ B.Bundle m v (SB.Bitstream d) → m (Bitstream d) unstreamChunks (B.Bundle (Stream step s0) _ _ _) = go s0 #else unstreamChunks ∷ ( G.Bitstream (SB.Bitstream d) , Monad m ) ⇒ Stream m (SB.Bitstream d) → m (Bitstream d) unstreamChunks (Stream step s0 _) = go s0 #endif where {-# INLINE go #-} go s = do r ← step s case r of Yield x s' → do xs ← go s' if null x then return xs else return $ Chunk x xs Skip s' → go s' Done → return Empty {-# RULES "Lazy Bitstream streamChunks/unstreamChunks fusion" ∀s. streamChunks (unId (unstreamChunks s)) = s #if MIN_VERSION_base(4,9,0) #else "Lazy Bitstream unstreamChunks/streamChunks fusion" ∀v. unId (unstreamChunks (streamChunks v)) = v #endif #-} #if MIN_VERSION_vector(0,11,0) inplace :: Monad m => (Stream m a -> Stream m b) -> (Size -> Size) -> B.Bundle m v a -> B.Bundle m v b inplace f g b = b `seq` B.fromStream (f (B.elements b)) (g (B.size b)) #endif -- Awful implementation to gain speed... {-# INLINEABLE packChunks #-} #if MIN_VERSION_vector(0,11,0) packChunks ∷ ∀d m v . (G.Bitstream (Packet d), Monad m) ⇒ B.Bundle m v (Packet d) → B.Bundle m v (SB.Bitstream d) packChunks = inplace (\(Stream st s0) -> Stream (step' st) (emptyChunk, 0, 0, Just s0)) sz' #else packChunks ∷ ∀d m. (G.Bitstream (Packet d), Monad m) ⇒ Stream m (Packet d) → Stream m (SB.Bitstream d) packChunks (Stream st s0 sz) = Stream (step' st) (emptyChunk, 0, 0, Just s0) (sz' sz) #endif where emptyChunk ∷ New.New SV.Vector (Packet d) {-# INLINE emptyChunk #-} emptyChunk = New.create (MVector.unsafeNew chunkSize) singletonChunk ∷ Packet d → New.New SV.Vector (Packet d) {-# INLINE singletonChunk #-} singletonChunk = writePacket emptyChunk 0 writePacket ∷ New.New SV.Vector (Packet d) → Int → Packet d → New.New SV.Vector (Packet d) {-# INLINE writePacket #-} writePacket ch len p = New.modify (\mv → MVector.write mv len p) ch newChunk ∷ G.Bitstream (Packet d) ⇒ New.New SV.Vector (Packet d) → Int → Int → SB.Bitstream d {-# INLINE newChunk #-} newChunk ch cLen bLen = SB.unsafeFromPackets bLen $ GV.new $ New.apply (MVector.take cLen) ch sz' ∷ Size -> Size {-# INLINE sz' #-} sz' v = case v of Exact n → Exact ((n + chunkSize - 1) `div` chunkSize) Max n → Max ((n + chunkSize - 1) `div` chunkSize) Unknown → Unknown {-# INLINE step' #-} step' step (ch, cLen, bLen, Just s) = do r ← step s case r of Yield p s' | cLen ≡ chunkSize → return $ Yield (newChunk ch cLen bLen) (singletonChunk p, 1, length p, Just s') | otherwise → return $ Skip (writePacket ch cLen p, cLen+1, bLen + length p, Just s') Skip s' → return $ Skip (ch , cLen , bLen , Just s') Done | cLen ≡ 0 → return Done | otherwise → return $ Yield (newChunk ch cLen bLen) ((⊥), (⊥), (⊥), Nothing) step' _ (_, _, _, Nothing) = return Done {-# INLINE unpackChunks #-} #if MIN_VERSION_vector(0,11,0) unpackChunks ∷ Bundle SV.Vector (SB.Bitstream d) → Bundle SV.Vector (Packet d) #else unpackChunks ∷ S.Stream (SB.Bitstream d) → S.Stream (Packet d) #endif unpackChunks = S.concatMap SB.streamPackets -- | /O(n)/ Convert a @'Bitstream' 'Left'@ into a @'Bitstream' -- 'Right'@. Bit directions only affect octet-based operations such as -- 'toByteString'. directionLToR ∷ Bitstream Left → Bitstream Right {-# INLINE directionLToR #-} directionLToR Empty = Empty directionLToR (Chunk x xs) = Chunk (SB.directionLToR x) (directionLToR xs) -- | /O(n)/ Convert a @'Bitstream' 'Right'@ into a @'Bitstream' -- 'Left'@. Bit directions only affect octet-based operations such as -- 'toByteString'. directionRToL ∷ Bitstream Right → Bitstream Left {-# INLINE directionRToL #-} directionRToL Empty = Empty directionRToL (Chunk x xs) = Chunk (SB.directionRToL x) (directionRToL xs) {- There are only 4 functions of the type Bool → Bool. * iterate id b == [b , b , b , b , ...] * iterate (const True ) _ == [True , True , True , True , ...] * iterate (const False) _ == [False, False, False, False, ...] * iterate not True == [True , False, True , False, ...] * iterate not False == [False, True , False, True , ...] As seen above, all of them are cyclic so we just replicate the first 8 bits i.e. a single Packet. Dunno when the given function involves unsafeInlineIO and produces random bits. -} -- | /O(n)/ 'iterate' @f x@ returns an infinite 'Bitstream' of -- repeated applications of @f@ to @x@: -- -- @ -- 'iterate' f x == [x, f x, f (f x), ...] -- @ iterate ∷ G.Bitstream (Packet d) ⇒ (Bool → Bool) → Bool → Bitstream d {-# INLINE iterate #-} iterate f b = xs where xs = Chunk x xs x = SB.fromPackets (SV.replicate chunkSize p) p = pack (L.take 8 (L.iterate f b)) -- | /O(n)/ 'repeat' @x@ is an infinite 'Bitstream', with @x@ the -- value of every bits. repeat ∷ G.Bitstream (Packet d) ⇒ Bool → Bitstream d {-# INLINE repeat #-} repeat b = xs where xs = Chunk x xs x = SB.fromPackets (SV.replicate chunkSize p) p = pack (L.replicate 8 b) -- | /O(n)/ 'cycle' ties a finite 'Bitstream' into a circular one, or -- equivalently, the infinite repetition of the original 'Bitstream'. -- It is the identity on infinite 'Bitstream's. cycle ∷ G.Bitstream (Bitstream d) ⇒ Bitstream d → Bitstream d {-# INLINE cycle #-} cycle Empty = emptyStream cycle ch = ch ⧺ cycle ch -- | /O(n)/ 'getContents' is equivalent to 'hGetContents' -- @stdin@. Will read /lazily/. getContents ∷ G.Bitstream (SB.Bitstream d) ⇒ IO (Bitstream d) {-# INLINE getContents #-} getContents = fmap fromByteString LS.getContents -- | /O(n)/ Write a 'Bitstream' to @stdout@, equivalent to 'hPut' -- @stdout@. putBits ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ Bitstream d → IO () {-# INLINE putBits #-} putBits = LS.putStr ∘ toByteString -- | The 'interact' function takes a function of type @'Bitstream' d -- -> 'Bitstream' d@ as its argument. The entire input from the stdin -- is lazily passed to this function as its argument, and the -- resulting 'Bitstream' is output on the stdout. interact ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ (Bitstream d → Bitstream d) → IO () {-# INLINE interact #-} interact = LS.interact ∘ lift' where {-# INLINE lift' #-} lift' f = toByteString ∘ f ∘ fromByteString -- | /O(n)/ Read an entire file lazily into a 'Bitstream'. readFile ∷ G.Bitstream (SB.Bitstream d) ⇒ FilePath → IO (Bitstream d) {-# INLINE readFile #-} readFile = fmap fromByteString ∘ LS.readFile -- | /O(n)/ Write a 'Bitstream' to a file. writeFile ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ FilePath → Bitstream d → IO () {-# INLINE writeFile #-} writeFile = (∘ toByteString) ∘ LS.writeFile -- | /O(n)/ Append a 'Bitstream' to a file. appendFile ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ FilePath → Bitstream d → IO () {-# INLINE appendFile #-} appendFile = (∘ toByteString) ∘ LS.appendFile -- | /O(n)/ Read entire handle contents /lazily/ into a -- 'Bitstream'. Chunks are read on demand, using the default chunk -- size. -- -- Once EOF is encountered, the 'Handle' is closed. hGetContents ∷ G.Bitstream (SB.Bitstream d) ⇒ Handle → IO (Bitstream d) {-# INLINE hGetContents #-} hGetContents = fmap fromByteString ∘ LS.hGetContents -- |@'hGet' h n@ reads a 'Bitstream' directly from the specified -- 'Handle' @h@. First argument @h@ is the 'Handle' to read from, and -- the second @n@ is the number of /octets/ to read, not /bits/. It -- returns the octets read, up to @n@, or null if EOF has been -- reached. -- -- If the handle is a pipe or socket, and the writing end is closed, -- 'hGet' will behave as if EOF was reached. -- {-# INLINE hGet #-} hGet ∷ G.Bitstream (SB.Bitstream d) ⇒ Handle → Int → IO (Bitstream d) hGet = (fmap fromByteString ∘) ∘ LS.hGet -- | /O(n)/ 'hGetNonBlocking' is similar to 'hGet', except that it -- will never block waiting for data to become available, instead it -- returns only whatever data is available. {-# INLINE hGetNonBlocking #-} hGetNonBlocking ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ Handle → Int → IO (Bitstream d) hGetNonBlocking = (fmap fromByteString ∘) ∘ LS.hGetNonBlocking -- | /O(n)/ Write a 'Bitstream' to the given 'Handle'. hPut ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ Handle → Bitstream d → IO () {-# INLINE hPut #-} hPut = (∘ toByteString) ∘ LS.hPut