-- | Cruncher interface @exomizer mem@ style. module Exomizer.Mem ( -- * Options & Info CrunchOptions , coEncoding , coMaxPasses , coMaxLen , coMaxOffset , coUseLiteralSequences , coFavourSpeed , defaultCrunchOptions , validCrunchOptions , CrunchInfo , ciLiteralSequencesUsed , ciNeededSafetyOffset , ciUsedEncoding -- * Direction , Direction(..) , ReadWrite(..) , forward , backwards -- * Mem crunchers , memCrunch , memCrunch_ -- * Join chunks , joinChunks ) where import Data.Bits import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.Int import Data.List (foldl1') import Data.Word import Exomizer.Data.Args import Exomizer.Data.Direction import Exomizer.Extern.Crunch -- | Crunch data @exomizer mem@ style. This function does not have automatic joining of multiple chunks, see 'joinChunks'. -- -- @since 1.0.0 memCrunch :: ReadWrite -> CrunchOptions -> (Int16, ByteString) -> (ByteString, CrunchInfo) {-# INLINABLE memCrunch #-} memCrunch (Forward `ReadWrite` Forward) co (la, bs) = let (bs', ci) = crunchBackwards co (BS.reverse bs) in (BS.pack [fromIntegral (la `shiftR` 8), fromIntegral la] `BS.append` BS.reverse bs', ci) memCrunch (Backwards `ReadWrite` Forward) co (la, bs) = let (bs', ci) = crunchBackwards co (BS.reverse bs) in (bs' `BS.append` BS.pack [fromIntegral la, fromIntegral (la `shiftR` 8)], ci) memCrunch (Forward `ReadWrite` Backwards) co (la, bs) = let (bs', ci) = crunchBackwards co bs la' = la + fromIntegral (BS.length bs) in (BS.pack [fromIntegral (la' `shiftR` 8), fromIntegral la'] `BS.append` BS.reverse bs', ci) memCrunch (Backwards `ReadWrite` Backwards) co (la, bs) = let (bs', ci) = crunchBackwards co bs la' = la + fromIntegral (BS.length bs) in (bs' `BS.append` BS.pack [fromIntegral la', fromIntegral (la' `shiftR` 8)], ci) -- | Identical to 'memCrunch' but does not return the 'CrunchInfo'. -- -- @since 1.0.0 memCrunch_ :: ReadWrite -> CrunchOptions -> (Int16, ByteString) -> ByteString {-# INLINABLE memCrunch_ #-} memCrunch_ readWrite args = fst . memCrunch readWrite args -- | Join the supplied chunks into one. The spaces in between are filled with the supplied byte. -- -- There is no check for overlap or if the result exceeds the memory space. -- -- @since 1.0.0 joinChunks :: Word8 -> [(Int16, ByteString)] -> (Int16, ByteString) joinChunks _ [] = (0, BS.empty) joinChunks fill chunks = foldl1' combine chunks where combine :: (Int16, ByteString) -> (Int16, ByteString) -> (Int16, ByteString) combine (la, ba) (lb, bb) | la > lb = combine (lb, bb) (la, ba) | la' + lba >= lb' = (la, BS.take (lb' - la') ba `BS.append` bb) | otherwise = (la, ba `BS.append` BS.replicate (lb' - (la' + lba)) fill `BS.append` bb) where la' = fromIntegral la lb' = fromIntegral lb lba = BS.length ba