{-# LANGUAGE BangPatterns , 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 import qualified Data.Vector.Fusion.Stream as S import Data.Vector.Fusion.Stream.Monadic (Stream(..), Step(..)) import Data.Vector.Fusion.Stream.Size 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 -- | '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 lazyStream ∷ G.Bitstream (SB.Bitstream d) ⇒ Bitstream d → S.Stream Bool {-# INLINE lazyStream #-} lazyStream = {-# CORE "Lazy Bitstream stream" #-} S.concatMap stream ∘ streamChunks lazyUnstream ∷ ( G.Bitstream (SB.Bitstream d) , G.Bitstream (Packet d) ) ⇒ S.Stream Bool → Bitstream d {-# INLINE lazyUnstream #-} 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 streamChunks ∷ ( G.Bitstream (SB.Bitstream d) , Monad m ) ⇒ Bitstream d → Stream m (SB.Bitstream d) {-# NOINLINE streamChunks #-} streamChunks ch0 = Stream step ch0 Unknown where {-# INLINE step #-} step Empty = return Done step (Chunk x xs) = return $ Yield x xs unstreamChunks ∷ ( G.Bitstream (SB.Bitstream d) , Monad m ) ⇒ Stream m (SB.Bitstream d) → m (Bitstream d) {-# NOINLINE unstreamChunks #-} unstreamChunks (Stream step s0 _) = go s0 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 "Lazy Bitstream unstreamChunks/streamChunks fusion" ∀v. unId (unstreamChunks (streamChunks v)) = v #-} -- Awful implementation to gain speed... packChunks ∷ ∀d m. (G.Bitstream (Packet d), Monad m) ⇒ Stream m (Packet d) → Stream m (SB.Bitstream d) {-# INLINEABLE packChunks #-} packChunks (Stream step s0 sz) = Stream step' (emptyChunk, 0, 0, Just s0) sz' 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 {-# INLINE sz' #-} sz' = case sz of Exact n → Exact ((n + chunkSize - 1) `div` chunkSize) Max n → Max ((n + chunkSize - 1) `div` chunkSize) Unknown → Unknown {-# INLINE 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 unpackChunks ∷ S.Stream (SB.Bitstream d) → S.Stream (Packet d) {-# INLINE unpackChunks #-} 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